preview
Desenvolvendo um sistema de Replay (Parte 52): Complicando as coisas (IV)

Desenvolvendo um sistema de Replay (Parte 52): Complicando as coisas (IV)

MetaTrader 5Exemplos | 7 junho 2024, 16:56
80 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Desenvolvendo um sistema de Replay (Parte 51): Complicando as coisas (III), fizemos algumas mudanças no nosso antigo indicador de mouse a fim de que ele viesse a ser utilizado de maneira adequada no nosso sistema de replay / simulação. Naquele mesmo artigo, demonstrei de maneira prática as diferenças envolvidas em se obter a ID de um gráfico via ChartOpen, que é uma função usada para que os programas possam dizer ao MetaTrader 5 para abrir um gráfico, e obter mesma ID de um gráfico já aberto, porém usando a função ChartID.

Acredito que tenha ficado bastante claro tal diferença, e as possibilidades ao longo prazo, a fim de poder fazer uso bem mais amplo da programação via MQL5.

Pois bem, apesar de todas aquelas mudanças efetuadas no indicador de mouse, e no indicador de controle. Ocorreram algumas dificuldades em integrar, ou melhor dizendo fazer com que o indicador de mouse e o indicador de controle, viessem a ter uma interação adequada. E o motivo, foi uma falha da minha parte, isto lá atras por conta que durante o processo de desenvolvimento do indicador de mouse, ignorei alguns detalhes, e estes fizeram com que a interação não ocorresse de forma adequada.

Sendo assim neste artigo iremos corrigir isto, mas não se preocupe, é algo bastante simples de ser feito, porém de grande necessidade. Já que uma vez que esta correção seja feita, você também poderá utilizar este mesmo indicador de mouse, em seus projetos pessoais. Tento assim um sistema de estudo personalizado ao mesmo tempo que uma forma bastante segura de interagir com o gráfico. Já que uma vez que o indicador de mouse esteja no modo estudo. Ele não irá permitir, que ocorra alguma interação, com outros objetos presentes no gráfico aconteçam. Mas para isto será preciso que você o integre ao seu código localmente.

Para demonstrar como esta integração deverá ser feita, isto neste primeiro momento, já que iremos fazer a mesma coisa com outras partes do sistema de replay / simulação. Iremos utilizar o indicador de controle, mas este mesmo indicador de controle, foi modificado a fim de permitir um sistema mais adequado. Já que anteriormente, estávamos utilizando o sistema base do MQL5. Mas iremos usar o MQL5 de uma maneira um pouco mais profunda, assim teremos algumas possibilidades que antes não nos era possível de ser obtidas.

A interação que iremos conseguir neste artigo, ainda será básica, mas será o suficiente para que você consiga compreender como integrar o indicador de mouse, aos seus programas e assim ter um sistema de estudos personalizado que ao mesmo tempo não interage de forma erradica a objetos colocados no gráfico. Então vamos iniciar este artigo, vendo o primeiro tópico, onde iremos abordar as modificações, e estas devem ser quase definitivas, no indicador de mouse.


Refinando o indicador de Mouse

Muito bem, como todo o código, e irei manter as coisas assim por hora, será colocado no artigo, e completamente na integra. Talvez você imagine que ele possa estar sendo replicado demais. No entanto, desta maneira, como o código está sendo colocado, fica mais fácil de explicar os detalhes envolvidos, assim se o código vier a fazer referência a alguma outra coisa, que não esteja presente aqui, você deverá buscar as referências nos artigos anteriores.

Desta maneira, terei completa e absoluta certeza de que você de fato vem acompanhando as explicações e muito provavelmente compreendendo como o código está sendo desenvolvido. Dada esta breve explicação, vamos ver o código do indicador de mouse. Este pode ser visto logo abaixo:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. #property description "This is an indicator for graphical studies using the mouse."
004. #property description "This is an integral part of the Replay / Simulator system."
005. #property description "However it can be used in the real market."
006. #property version "1.52"
007. #property icon "/Images/Market Replay/Icons/Indicators.ico"
008. #property link "https://www.mql5.com/pt/articles/11925"
009. #property indicator_chart_window
010. #property indicator_plots 0
011. #property indicator_buffers 1
012. //+------------------------------------------------------------------+
013. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
014. //+------------------------------------------------------------------+
015. C_Study *Study     = NULL;
016. //+------------------------------------------------------------------+
017. input long user00  = 0;                                    //ID
018. input C_Study::eStatusMarket user01 = C_Study::eAuction;   //Market Status
019. input color user02 = clrBlack;                             //Price Line
020. input color user03 = clrPaleGreen;                         //Positive Study
021. input color user04 = clrLightCoral;                        //Negative Study
022. //+------------------------------------------------------------------+
023. C_Study::eStatusMarket m_Status;
024. int m_posBuff = 0;
025. double m_Buff[];
026. //+------------------------------------------------------------------+
027. int OnInit()
028. {
029.    ResetLastError();
030.    Study = new C_Study(user00, "Indicator Mouse Study", user02, user03, user04);
031.    if (_LastError != ERR_SUCCESS) return INIT_FAILED;
032.    if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
033.    {
034.            MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
035.            OnBookEvent((*Study).GetInfoTerminal().szSymbol);
036.            m_Status = C_Study::eCloseMarket;
037.    }else
038.            m_Status = user01;
039.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
040.    ArrayInitialize(m_Buff, EMPTY_VALUE);
041.    
042.    return INIT_SUCCEEDED;
043. }
044. //+------------------------------------------------------------------+
045. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
046. {
047.    m_posBuff = rates_total - 5;
048.    (*Study).Update(m_Status);      
049.    
050.    return rates_total;
051. }
052. //+------------------------------------------------------------------+
053. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
054. {
055.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
056.    SetBuffer();
057.    
058.    ChartRedraw((*Study).GetInfoTerminal().ID);
059. }
060. //+------------------------------------------------------------------+
061. void OnBookEvent(const string &symbol)
062. {
063.    MqlBookInfo book[];
064.    C_Study::eStatusMarket loc = m_Status;
065.    
066.    if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
067.    MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
068.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading);
069.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
070.            if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
071.    if (loc != m_Status) (*Study).Update(m_Status);
072. }
073. //+------------------------------------------------------------------+
074. void OnDeinit(const int reason)
075. {
076.    if (reason != REASON_INITFAILED)
077.    {
078.            if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
079.                    MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
080.    }
081.    delete Study;
082. }
083. //+------------------------------------------------------------------+
084. inline void SetBuffer(void)
085. {
086.    uCast_Double Info;
087.    
088.    m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
089.    m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
090.    Info._datetime = (*Study).GetInfoMouse().Position.dt;
091.    m_Buff[m_posBuff + 1] = Info.dValue;
092.    Info._int[0] = (*Study).GetInfoMouse().Position.X_Adjusted;
093.    Info._int[1] = (*Study).GetInfoMouse().Position.Y_Adjusted;
094.    m_Buff[m_posBuff + 2] = Info.dValue;
095.    Info._int[0] = (*Study).GetInfoMouse().Position.X_Graphics;
096.    Info._int[1] = (*Study).GetInfoMouse().Position.Y_Graphics;
097.    m_Buff[m_posBuff + 3] = Info.dValue;
098.    Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
099.    m_Buff[m_posBuff + 4] = Info.dValue;
100. }
101. //+------------------------------------------------------------------+

Código fonte do Indicador de Mouse

Muito provavelmente você não consegue notar as diferenças entre este código e os anteriores. Como eu havia dito, as mudanças são muito simples e sutis, mas elas de fato se encontram aí.

A primeira delas, e talvez a única de fato, seja no index do buffer a ser escrito pelo indicador. Você pode notar isto ao observar na linha 47 do código, que o valor de deslocamento que antes era de quatro, agora é de cinco. Mas por que desta mudança ?!?! Este incremento em uma unidade, é necessário para comportar um estilo mais amplo de cobertura. Mas para compreender melhor, vamos passear até a linha 84, onde temos a função responsável por montar o buffer do indicador.

Muito provavelmente, você não irá notar diferença nesta função com as vistas no passado. De fato, e mais uma vez as mudanças são muito sútis. Mas é necessário que você as compreenda a fim de poder tirar proveito delas em seus projetos pessoais, que você poderá desejar integrar este indicador ao seu modelo especifico.

Especificamente, a partir da linha 92 a coisa começa a ficar diferente, não tão diferente assim, mas observe com atenção. Por que estou declarando duas variáveis X e duas variáveis Y ?!?! E por que uma tem como nome Adjusted e a outra Graphics ?!?! Pois bem, é justamente este o ponto. Antes o indicador apenas retornava uma variável gráfica ajustada em termos de tempo e preço. Mas isto não é adequado em todos os momentos. Existem situações em que precisamos de fato do valor da coordenada gráfica e não do valor ajustado. Mas para satisfazer ambas situações, o indicador irá retornar ambos os valores, assim você poderá fazer o melhor uso que lhe seja desejado.

Mas observem também que na posição zero do buffer, teremos o valor do preço, e na posição um o valor do tempo. Decidi manter isto por motivos de retro compatibilidade com outras coisas que eu já faço uso pessoal. Mas você não precisa de fato ler o buffer da maneira como ele foi construído. Lembre-se, existe uma forma mais simples de em um nível mais alto, que é usando a classe do Mouse, mas se ainda assim você desejar ler os dados do buffer diretamente, deve entender como ele foi de fato construído. E para isto, entender esta função presente na linha 84 é primordial.

Muito bem, mas as mudanças não ficam apenas aí, você precisa compreender que ao fazer uma mudança no código, esta poderá afetar grande aparte do restante do código. Apesar de tudo, na classe responsável por criar e fazer os estudos, a mudança não foi assim tão drástica, mas mesmo assim merece algum tipo de explicação. O código da classe de estudos pode ser visto logo a seguir.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\C_Mouse.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_"
007. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
008. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2"
009. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3"
010. //+------------------------------------------------------------------+
011. class C_Study : public C_Mouse
012. {
013.    private :
014. //+------------------------------------------------------------------+
015.            struct st00
016.            {
017.                    eStatusMarket Status;
018.                    MqlRates      Rate;
019.                    string        szInfo;
020.                    color         corP,
021.                                  corN;
022.                    int           HeightText;
023.            }m_Info;
024. //+------------------------------------------------------------------+
025.            const datetime GetBarTime(void)
026.                    {
027.                            datetime dt;
028.                            u_Interprocess info;
029.                            int i0 = PeriodSeconds();
030.                            
031.                            if (m_Info.Status == eInReplay)
032.                            {
033.                                    if (!GlobalVariableGet(def_GlobalVariableServerTime, info.df_Value)) return ULONG_MAX;
034.                                    if ((dt = info.ServerTime) == ULONG_MAX) return ULONG_MAX;
035.                            }else dt = TimeCurrent();
036.                            if (m_Info.Rate.time <= dt)
037.                                    m_Info.Rate.time = (datetime)(((ulong) dt / i0) * i0) + i0;
038. 
039.                            return m_Info.Rate.time - dt;
040.                    }
041. //+------------------------------------------------------------------+
042.            void Draw(void)
043.                    {
044.                            double v1;
045.                            
046.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 18);
047.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1);
048.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1);
049.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo);
050.                            v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / GetInfoMouse().Position.Price) * 100.0), 2);
051.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
052.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
053.                            v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0)) * 100.0), 2);
054.                            ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
055.                            ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
056.                    }
057. //+------------------------------------------------------------------+
058.    public  :
059. //+------------------------------------------------------------------+
060.            C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
061.                    :C_Mouse(IdParam, szShortName, corH, corP, corN)
062.                    {
063.                            if (_LastError != ERR_SUCCESS) return;
064.                            ZeroMemory(m_Info);
065.                            m_Info.Status = eCloseMarket;
066.                            m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1));
067.                            m_Info.corP = corP;
068.                            m_Info.corN = corN;
069.                            CreateObjectInfo(2, 110, def_ExpansionBtn1, clrPaleTurquoise);
070.                            CreateObjectInfo(2, 53, def_ExpansionBtn2);
071.                            CreateObjectInfo(58, 53, def_ExpansionBtn3);
072.                    }
073. //+------------------------------------------------------------------+
074.            void Update(const eStatusMarket arg)
075.                    {
076.                            datetime dt;
077.                            
078.                            switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
079.                            {
080.                                    case eCloseMarket : m_Info.szInfo = "Closed Market";
081.                                            break;
082.                                    case eInReplay    :
083.                                    case eInTrading   :
084.                                            if ((dt = GetBarTime()) < ULONG_MAX)
085.                                            {
086.                                                    m_Info.szInfo = TimeToString(dt, TIME_SECONDS);
087.                                                    break;
088.                                            }
089.                                    case eAuction     : m_Info.szInfo = "Auction";
090.                                            break;
091.                                    default           : m_Info.szInfo = "ERROR";
092.                            }
093.                            Draw();
094.                    }
095. //+------------------------------------------------------------------+
096. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
097.                    {
098.                            C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
099.                            if (id == CHARTEVENT_MOUSE_MOVE) Draw();
100.                    }
101. //+------------------------------------------------------------------+
102. };
103. //+------------------------------------------------------------------+
104. #undef def_ExpansionBtn3
105. #undef def_ExpansionBtn2
106. #undef def_ExpansionBtn1
107. #undef def_ExpansionPrefix
108. #undef def_MousePrefixName
109. //+------------------------------------------------------------------+

Código fonte da classe C_Study

Basicamente nesta classe, as únicas mudanças reais, ocorreram na função Draw, que se encontra na linha 42. Tais mudanças visam manter a lógica de estudos baseada no tempo e no preço. Porém utilizando objetos, cujo sistema de coordenadas é do tipo gráfico. Este tipo de coisa foi explicado, quando este indicador de mouse foi criado a alguns artigos atras, nesta mesma sequência. Lá expliquei o motivo e como fazer para trabalhar em coordenadas de Preço - Tempo e coordenadas gráficas do tipo X - Y.

Então para manter esta retro compatibilidade, fazemos uso das coordenadas gráficas ajustadas. Isto pode ser visto ao se olhar as linhas 46 a 48. Mas se você estiver usando outros objetos que precisam usar coordenadas gráficas, no entanto você as quer seguindo as coordenadas de Preço - Tempo. Você simplesmente deverá fazer uso das coordenadas gráficas ajustadas, da mesma forma como está sendo mostrado aqui. Caso você mude este sistema ajustado para o sistema gráfico, que não estará ajustado, o estudo pode ficar ligeiramente diferente.

Talvez seja adequado você experimentar isto, assim irá ficar mais claro o que acabei de falar.

Mas para terminar esta parte referente ao indicador de mouse, vamos ver a classe responsável por manter o sistema base. Assim você poderá entender melhor a diferença entre as coordenadas gráficas ajustadas e as não ajustadas. E depois iremos passar para o código do indicador de controle, para entender o motivo de todas estas mudanças. O código da classe do mouse, pode ser visto na integra logo abaixo. E espero que não venha a precisar modifica-lo mais uma vez.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. #include "Interprocess.mqh"
006. //+------------------------------------------------------------------+
007. #define def_MousePrefixName "MouseBase_"
008. #define def_NameObjectLineH def_MousePrefixName + "H"
009. #define def_NameObjectLineV def_MousePrefixName + "TV"
010. #define def_NameObjectLineT def_MousePrefixName + "TT"
011. #define def_NameObjectStudy def_MousePrefixName + "TB"
012. //+------------------------------------------------------------------+
013. class C_Mouse : public C_Terminal
014. {
015.    public  :
016.            enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
017.            enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
018.            struct st_Mouse
019.            {
020.                    struct st00
021.                    {
022.                            int      X_Adjusted,
023.                                     Y_Adjusted,
024.                                     X_Graphics,
025.                                     Y_Graphics;
026.                            double   Price;
027.                            datetime dt;
028.                    }Position;
029.                    uint    ButtonStatus;
030.                    bool    ExecStudy;
031.            };
032. //+------------------------------------------------------------------+
033.    protected:
034.            enum eEventsMouse {ev_HideMouse, ev_ShowMouse};
035. //+------------------------------------------------------------------+
036.            void CreateObjectInfo(int x, int w, string szName, color backColor = clrNONE) const
037.                    {
038.                            if (m_Mem.szShortName != NULL) return;
039.                            CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE);
040.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_STATE, true);
041.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BORDER_COLOR, clrBlack);
042.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_COLOR, clrBlack);
043.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_BGCOLOR, backColor);
044.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_FONT, "Lucida Console");
045.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_FONTSIZE, 10);
046.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
047.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, x);
048.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1);
049.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XSIZE, w); 
050.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YSIZE, 18);
051.                    }
052. //+------------------------------------------------------------------+
053.    private :
054.            enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
055.            struct st01
056.            {
057.                    st_Mouse Data;
058.                    color    corLineH,
059.                             corTrendP,
060.                             corTrendN;
061.                    eStudy   Study;
062.            }m_Info;
063.            struct st_Mem
064.            {
065.                    bool     CrossHair,
066.                             IsFull;
067.                    datetime dt;
068.                    string   szShortName;
069.            }m_Mem;
070.            bool m_OK;
071. //+------------------------------------------------------------------+
072.            void GetDimensionText(const string szArg, int &w, int &h)
073.                    {
074.                            TextSetFont("Lucida Console", -100, FW_NORMAL);
075.                            TextGetSize(szArg, w, h);
076.                            h += 5;
077.                            w += 5;
078.                    }
079. //+------------------------------------------------------------------+
080.            void CreateStudy(void)
081.                    {
082.                            if (m_Mem.IsFull)
083.                            {
084.                                    CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH);
085.                                    CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH);
086.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_WIDTH, 2);
087.                                    CreateObjectInfo(0, 0, def_NameObjectStudy);
088.                            }
089.                            m_Info.Study = eStudyCreate;
090.                    }
091. //+------------------------------------------------------------------+
092.            void ExecuteStudy(const double memPrice)
093.                    {
094.                            double v1 = GetInfoMouse().Position.Price - memPrice;
095.                            int w, h;
096.                            
097.                            if (!CheckClick(eClickLeft))
098.                            {
099.                                    m_Info.Study = eStudyNull;
100.                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);
101.                                    if (m_Mem.IsFull) ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName + "T");
102.                            }else if (m_Mem.IsFull)
103.                            {
104.                                    string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ",
105.                                            MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0)));
106.                                    GetDimensionText(sz1, w, h);
107.                                    ObjectSetString(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_TEXT, sz1);                                                                                                                          
108.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
109.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XSIZE, w);
110.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YSIZE, h);
111.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X_Adjusted - w);
112.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h));                          
113.                                    ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price);
114.                                    ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
115.                            }
116.                            m_Info.Data.ButtonStatus = eKeyNull;
117.                    }
118. //+------------------------------------------------------------------+
119.    public  :
120. //+------------------------------------------------------------------+
121.            C_Mouse(const long id, const string szShortName)
122.                    :C_Terminal(id),
123.                    m_OK(false)
124.                    {
125.                            m_Mem.szShortName = szShortName;
126.                    }
127. //+------------------------------------------------------------------+
128.            C_Mouse(const long id, const string szShortName, color corH, color corP, color corN)
129.                    :C_Terminal(id)
130.                    {
131.                            if (!(m_OK = IndicatorCheckPass(szShortName))) SetUserError(C_Terminal::ERR_Unknown);
132.                            if (_LastError != ERR_SUCCESS) return;
133.                            m_Mem.szShortName = NULL;
134.                            m_Mem.CrossHair = (bool)ChartGetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL);
135.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, true);
136.                            ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, false);
137.                            ZeroMemory(m_Info);
138.                            m_Info.corLineH  = corH;
139.                            m_Info.corTrendP = corP;
140.                            m_Info.corTrendN = corN;
141.                            m_Info.Study = eStudyNull;
142.                            if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
143.                                    CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
144.                    }
145. //+------------------------------------------------------------------+
146.            ~C_Mouse()
147.                    {
148.                            if (!m_OK) return;
149.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, 0, false);
150.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_MOUSE_MOVE, false);
151.                            ChartSetInteger(GetInfoTerminal().ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
152.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_MousePrefixName);
153.                    }
154. //+------------------------------------------------------------------+
155. inline bool CheckClick(const eBtnMouse value) 
156.                    {
157.                            return (GetInfoMouse().ButtonStatus & value) == value;
158.                    }
159. //+------------------------------------------------------------------+
160. inline const st_Mouse GetInfoMouse(void)
161.                    {
162.                            if (m_Mem.szShortName != NULL)
163.                            {
164.                                    double Buff[];
165.                                    uCast_Double loc;
166.                                    int handle = ChartIndicatorGet(GetInfoTerminal().ID, 0, m_Mem.szShortName);
167.                                    
168.                                    ZeroMemory(m_Info.Data);
169.                                    if (CopyBuffer(handle, 0, 0, 5, Buff) == 5)
170.                                    {
171.                                            m_Info.Data.Position.Price = Buff[0];
172.                                            loc.dValue = Buff[1];
173.                                            m_Info.Data.Position.dt = loc._datetime;
174.                                            loc.dValue = Buff[2];
175.                                            m_Info.Data.Position.X_Adjusted = loc._int[0];
176.                                            m_Info.Data.Position.Y_Adjusted = loc._int[1];
177.                                            loc.dValue = Buff[3];
178.                                            m_Info.Data.Position.X_Graphics = loc._int[0];
179.                                            m_Info.Data.Position.Y_Graphics = loc._int[1];
180.                                            loc.dValue = Buff[4];
181.                                            m_Info.Data.ButtonStatus = loc._char[0];
182.                                            IndicatorRelease(handle);
183.                                    }
184.                            }
185. 
186.                            return m_Info.Data;
187.                    }
188. //+------------------------------------------------------------------+
189. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
190.                    {
191.                            int w = 0;
192.                            static double memPrice = 0;
193.                            
194.                            if (m_Mem.szShortName == NULL) switch (id)
195.                            {
196.                                    case (CHARTEVENT_CUSTOM + ev_HideMouse):
197.                                            if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
198.                                            break;
199.                                    case (CHARTEVENT_CUSTOM + ev_ShowMouse):
200.                                            if (m_Mem.IsFull) ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
201.                                            break;
202.                                    case CHARTEVENT_MOUSE_MOVE:
203.                                            ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X_Graphics = (int)lparam, m_Info.Data.Position.Y_Graphics = (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
204.                                            if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price));
205.                                            m_Info.Data.Position.dt = AdjustTime(m_Info.Data.Position.dt);
206.                                            ChartTimePriceToXY(GetInfoTerminal().ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, m_Info.Data.Position.X_Adjusted, m_Info.Data.Position.Y_Adjusted);
207.                                            if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
208.                                            m_Info.Data.ButtonStatus = (uint) sparam;
209.                                            if (CheckClick(eClickMiddle))
210.                                                    if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
211.                                            if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
212.                                            {
213.                                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
214.                                                    if (m_Mem.IsFull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price);
215.                                                    m_Info.Study = eStudyExecute;
216.                                            }
217.                                            if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
218.                                            m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
219.                                            break;
220.                                    case CHARTEVENT_OBJECT_DELETE:
221.                                            if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
222.                                            break;
223.                            }
224.                    }
225. //+------------------------------------------------------------------+
226. };
227. //+------------------------------------------------------------------+
228. #undef def_NameObjectLineV
229. #undef def_NameObjectLineH
230. #undef def_NameObjectLineT
231. #undef def_NameObjectStudy
232. //+------------------------------------------------------------------+

Código fonte da classe C_Mouse

Você pode notar que a coisa aqui pode parecer tensa. Mas não se deixe levar pela primeira impressão. Vamos os detalhes iniciais. Bem, os includes presentes nas linhas 4 e 5, podem ser obtidas nos artigos passados. Já que as mesmas não sofreram nenhuma mudança, não faz sentido as replicar aqui novamente.

Descendo um pouco mais, vemos que entre as linhas 22 a 25, temos as declarações das variáveis vem estamos observando nos códigos anteriores. Atenção ao fato de que elas fazem parte de uma estrutura e que esta estrutura é pública. Sendo assim poderemos fazer uso desta mesma estrutura depois, tornando o código mais legível e por consequência de nível mais alto. Estas mesmas variáveis somente são usadas novamente nas linhas 110 e 111, a fim de promover um posicionamento adequado para alguns objetos.

Mas é na linha 157 que a coisa fica de fato interessante. Lembra de quando mencionei, neste mesmo artigo, que você não precisa de fato saber como o buffer foi montado, que podemos fazer uso da classe de mouse a fim de conseguir os valores que queremos ?!?! Pois bem, é justamente usando está função vista na linha 157, que torna isto possível. A mesma é capaz de se adequar e responder de forma correta um pedido de leitura do buffer, retornando as informações via a estrutura que foi mencionada a pouco. Assim as coisas se tornam bem mais simples de serem programadas.

No entanto, observe que da mesma forma como o buffer foi criado, aqui precisamos ir na direção inversa, a fim de conseguir obter as informações que estão presentes no buffer. Então caso tudo ocorra de maneira correta, esta função GetInfoMouse irá retornar os dados referentes ao posicionamento e cliques do mouse. Podendo ser usada em qualquer programa que você esteja desenvolvendo ou mesmo o próprio indicador de mouse. A interface é a mesma, facilitando muito o trabalho de manutenção e compreensão do código.

Logo depois desta função de retorno de informações do mouse, temos uma outra, talvez a mais importante de todas, já que ela irá responder as interações que ocorrerem no mouse. Esta função, a DispatchMessage, pode ser vista na linha 186. Mas basicamente ela está, quase igual era antes, tendo passado apenas por algumas melhorias, e estas melhorias, fazem justamente o que queremos e precisamos.

Vamos entender uma coisa: O MetaTrader 5, faz uso de um sistema de mensagens muito parecido com a do sistema operacional Windows. Por conta disto, as informações são repassadas ao nosso programa quase da mesma forma que seria feito se ele estivesse sendo executado do Windows. Saber programação Windows nestas horas ajuda bastante. Mas apesar da informação ser repassada da forma como é necessário, muitas das vezes ela está no tipo errado, ou está em um formato estranho para muitos, que não tem tanta familiaridade em programação.

Assim sendo, quando o MetaTrader 5, envia um evento de mouse para o nosso programa, ele compacta os dados do mouse na própria mensagem. Isto pode ser visto na linha 200, onde o que de fato acontece é que o MetaTrader 5, repassa exatamente o que o Windows informa para ele. Assim sendo o MetaTrader 5, não nos informa as coordenadas de mouse baseadas no preço e tempo, mas sim em termos gráficos. Neste momento temos as coordenadas gráficas que precisamos, estas não são coordenadas ajustadas, elas de fato representam a posição do mouse na janela. Não no gráfico, mas na janela. No entanto é preciso explicar uma coisa aqui. Quando digo janela, estou me referindo as janelas gráficas, para entender isto você precisa compreender programação Windows, e isto foge do escopo deste artigo. Uma vez que temos esta posição, que nos é informada, podemos usar o MQL5, para as tornar posições em termos de preço e tempo.

Mas existe um detalhe, tanto a posição de preço, quando a posição de tempo, NÃO serão ajustadas automaticamente pelo MetaTrader 5, ou pelo MQL5. Por conta disto objetos que usam estas coordenadas para serem posicionados, podem ficar ligeiramente fora do ponto, não tendo assim uma ligação direta com as barras presentes no gráfico. Para corrigir isto, usamos as linhas 201 que ajusta o preço, e a linha 202 onde ajustamos o tempo. Desta maneira a correspondência entre a posição e a barra presente no gráfico.

Mas como em alguns momentos precisamos que os valores X e Y estejam ajustados ao Preço e Tempo, fazemos uso da linha 203. Desta forma agora teremos os valores ajustados. Antes não era feita esta distinção entre o valor gráfico ajustado e o valor não ajustado. No entanto ao tentar manipular as coisas e integrar o indicador de mouse ao indicador de controle, foi preciso fazer esta diferenciação. Com isto fechamos o indicador de mouse e podemos voltar a nossa atenção ao indicador de controle.


Um novo indicador de controle

Como eu disse no início deste artigo, vamos fazer um uso mais intenso do MQL5, a fim de promover algumas melhorias no indicador de controle. Estas melhorias irão nos ajudar a deixar a coisa um pouco mais agradáveis. Então no anexo, você terá acesso as novas figuras que usaremos. Isto para podermos conseguir fazer e gerar a transparência. Então para deixar bastante claro como proceder para conseguir isto, novos bitmaps serão usados.

Como você já pode estar pensando. Sim foi necessário criar uma nova classe para isto. Mas não se preocupe, prometo que até o final do artigo irei mostrar a interação básica.

O código fonte da classe, pode ser visto logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "C_Terminal.mqh"
05. //+------------------------------------------------------------------+
06. #define def_MaxImages 2
07. //+------------------------------------------------------------------+
08. class C_DrawImage : protected C_Terminal
09. {
10. //+------------------------------------------------------------------+
11.     private :
12.             struct st_00
13.             {
14.                     int  widthMap,
15.                          heightMap;
16.                     uint Map[];
17.             }m_InfoImage[def_MaxImages];
18.             uint    m_Pixels[];
19.             string  m_szObjName,
20.                     m_szRecName;
21. //+------------------------------------------------------------------+
22.             void ReSizeImage(const int w, const int h, const uchar v, const int what)
23.                     {
24. #define _Transparency(A) (((A > 100 ? 100 : (100 - A)) * 2.55) / 255.0)
25.                             double fx = (w * 1.0) / m_InfoImage[what].widthMap;
26.                             double fy = (h * 1.0) / m_InfoImage[what].heightMap;
27.                             uint pyi, pyf, pxi, pxf, tmp;
28.                             uint uc;
29. 
30.                             ArrayResize(m_Pixels, w * h);
31.                             for (int cy = 0, y = 0; cy < m_InfoImage[what].heightMap; cy++, y += m_InfoImage[what].widthMap)
32.                             {
33.                                     pyf = (uint)(fy * cy) * w;
34.                                     tmp = pyi = (uint)(fy * (cy - 1)) * w;
35.                                     for (int x = 0; x < m_InfoImage[what].widthMap; x++)
36.                                     {
37.                                             pxf = (uint)(fx * x);
38.                                             pxi = (uint)(fx * (x - 1));
39.                                             uc = (uchar(double((uc = m_InfoImage[what].Map[x + y]) >> 24) * _Transparency(v)) << 24) | uc & 0x00FFFFFF;
40.                                             m_Pixels[pxf + pyf] = uc;
41.                                             for (pxi++; pxi < pxf; pxi++) m_Pixels[pxi + pyf] = uc;
42.                                     }
43.                                     for (pyi += w; pyi < pyf; pyi += w) 
44.                                             for (int x = 0; x < w; x++)
45.                                                     m_Pixels[x + pyi] = m_Pixels[x + tmp];
46.                             }
47. #undef _Transparency
48.                     }
49. //+------------------------------------------------------------------+
50.     public  :
51. //+------------------------------------------------------------------+
52.             C_DrawImage(long id, int sub, string szObjName, const color cFilter, const string szFile1, const string szFile2 = NULL)
53.                     :C_Terminal(id),
54.                     m_szObjName(NULL),
55.                     m_szRecName(NULL)
56.                     {
57.                             if (!ObjectCreate(GetInfoTerminal().ID, m_szObjName = szObjName, OBJ_BITMAP_LABEL, sub, 0, 0)) SetUserError(C_Terminal::ERR_Unknown);
58.                             m_szRecName = "::" + m_szObjName;                               
59.                             for (int c0 = 0; (c0 < def_MaxImages) && (_LastError == ERR_SUCCESS); c0++)
60.                             {
61.                                     ResourceReadImage((c0 == 0 ? szFile1 : (szFile2 == NULL ? szFile1 : szFile2)), m_InfoImage[c0].Map, m_InfoImage[c0].widthMap, m_InfoImage[c0].heightMap);
62.                                     ArrayResize(m_Pixels, m_InfoImage[c0].heightMap * m_InfoImage[c0].widthMap);
63.                                     ArrayInitialize(m_Pixels, 0);
64.                                     for (int c1 = (m_InfoImage[c0].heightMap * m_InfoImage[c0].widthMap) - 1; c1 >= 0; c1--)
65.                                             if ((m_InfoImage[c0].Map[c1] & 0x00FFFFFF) != cFilter) m_Pixels[c1] = m_InfoImage[c0].Map[c1];
66.                                     ArraySwap(m_InfoImage[c0].Map, m_Pixels);
67.                             }
68.                             ArrayResize(m_Pixels, 1);
69.                     }
70. //+------------------------------------------------------------------+
71.             ~C_DrawImage()
72.                     {
73.                             for (int c0 = 0; c0 < def_MaxImages; c0++)
74.                                     ArrayFree(m_InfoImage[c0].Map);
75.                             ArrayFree(m_Pixels);
76.                             ObjectDelete(GetInfoTerminal().ID, m_szObjName);
77.                             ResourceFree(m_szRecName);
78.                     }
79. //+------------------------------------------------------------------+
80.             void Paint(const int x, const int y, const int w, const int h, const uchar cView, const int what)
81.                     {
82.                             
83.                             if ((m_szRecName == NULL) || (what < 0) || (what >= def_MaxImages)) return;
84.                             ReSizeImage(w, h, cView, what);
85.                             ObjectSetInteger(GetInfoTerminal().ID, m_szObjName, OBJPROP_XDISTANCE, x);
86.                             ObjectSetInteger(GetInfoTerminal().ID, m_szObjName, OBJPROP_YDISTANCE, y);
87.                             if (ResourceCreate(m_szRecName, m_Pixels, w, h, 0, 0, 0, COLOR_FORMAT_ARGB_NORMALIZE))
88.                             {
89.                                     ObjectSetString(GetInfoTerminal().ID, m_szObjName, OBJPROP_BMPFILE, m_szRecName);
90.                                     ChartRedraw(GetInfoTerminal().ID);
91.                             }
92.                     }
93. //+------------------------------------------------------------------+
94. };
95. //+------------------------------------------------------------------+
96. #undef def_MaxImages
97. //+------------------------------------------------------------------+

Código fonte da classe C_DrawImage

Observe que o código é bem compacto. Isto por conta que não faremos uso de coisas externas. Vamos utilizar o MQL5 e tudo que ele pode nos promover de ajuda. Basicamente esta classe está promovendo uma forma alternativa de fazer uso dos objetos OBJ_BITMAP_LABEL ou dos OBJ_BITMAP. Ambos os tipos fazem parte da biblioteca padrão do MQL5, mas eles não têm uma forma para que possamos usar transparência, e pontos invisíveis na imagem. Sendo assim, criamos a classe que pode ser vista acima, e desta forma poderemos estender as coisas ao que queremos e precisamos.

Esta classe é tão simples, que você nota que fazemos uso de apenas quatro procedimentos, e todos eles bastante simples. Bem, mas vamos a uma explicação rápida de como as coisas funcionam aqui.

Na linha 6 declaramos o número máximo de imagens que iremos usar. Você pode aumentar este número, mas será preciso mexer um pouco no código. Mas logo irei mostrar onde você deverá mexer. Então entramos no código da classe, isto na linha 8. Atenção ao fato de ela está herdando a classe de terminal. Esta pode ser vista nos artigos anteriores.

Logo depois na linha 11, usamos uma clausula privativa, informando que os dados declarados serão privados ao código interno da classe. Você não precisa de fato mexer nesta parte, onde estes dados são declarados. Mas na linha 22 entramos no procedimento de pré renderização, onde ajustamos o tamanho da imagem, e também o nível de transparência. Este nível vai de 0% a 100% sendo modificado em passos inteiros, ou seja, de 1 em 1. A definição na linha 24 garante que iremos ter a conversão correta da transparência da imagem.

Aqui fazemos uma ampliação ou redução na resolução da imagem. Já que se trata de imagens pequenas, não vi necessidade de adicionar um sistema de anti alising. Então se a ampliação for muito grande, pode acontecer da imagem ficar muito serrilhada. Se você deseja fazer a ampliação de imagens, por qualquer motivo, deve pensar em adicionar um cálculo a fim de promover o anti alising da imagem, mas para o proposito que iremos usar, este sistema é adequado.

Na linha 52 temos o constructor da classe. Aqui neste ponto, é que você deverá aumentar o número parâmetros, caso queira trabalhar com mais imagens. Mas novamente, a ideia aqui é apenas estender o que é possível fazer dentro do MQL5. Por tanto duas imagens já nos será o suficiente.

Neste constructor iremos acessar as imagens na linha 61, estas serão referenciadas como sendo recursos. Lembre-se nosso sistema não irá precisar de coisas externas, podendo apenas os executáveis serem transportados. A linha 63 garante que a imagem é 100% transparente, mas na linha 64 entramos em um laço, que irá varrer a imagem carregada, e a cada interação iremos testar na linha 65 se a cor indicada para ser transparente foi ou não encontrada. Caso isto seja verdadeiro, a cor não será adicionada, caso seja falso ela será adicionada.

Na linha 66, fazemos uso de uma função do MQL5, a fim de trocar a imagem original pela imagem modificada. Assim fazemos as coisas da maneira que seja a mais rápida possível. Já na linha 68, deixamos o sistema limpo.

O destructor que inicia na linha 71, serve para devolver todos os recursos, no caso memória de volta para o sistema, liberando assim estes recursos para serem utilizados por outros programas, não necessitando assim de muitos detalhes e explicações.

Na linha 80 temos o procedimento de pintura. Este irá fazer com que a imagem seja desenhada no gráfico na posição e nas dimensões indicadas, isto é informado pelos quatro primeiros parâmetros. O quinto parâmetro da chamada deverá ser um valor entre 0 e 100, onde 0 é sem transparência e 100, transparência total. O sexto parâmetro irá dizer qual das imagens será de fato apresentada, o valor deve sempre iniciar em 0, sendo este o index da primeira imagem, e um valor máximo menos 1, ou seja, estaremos utilizando o mesmo sistema que é visto ao fazer uso dos objetos OBJ_BITMAP_LABEL e OBJ_BITMAP, onde informamos o index da imagem, só que aqui podemos ter mais de 2, desde que você modifique os pontos que indiquei. Basicamente esta função de pintura é auto explicativa, não precisando de nenhuma mudança extra caso você pretenda estender ainda mais as coisas.

Os únicos locais que precisarão de mudanças é o constructor e a definição presente na linha 6. Agora caso você queira carregar uma imagem diretamente de um arquivo, será necessário implementar isto também. Mas para o que queremos fazer, esta classe já está de excelente tamanho.

Muito bem, agora que já vimos isto, podemos passar para o código do indicador e da classe de controle. Mas antes de vermos o código da classe de controle, vamos dar uma parada rápida no código do indicador. Este pode ser visto logo abaixo na integra.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.52"
07. #property link "https://www.mql5.com/pt/articles/11925"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. C_Controls *control = NULL;
14. //+------------------------------------------------------------------+
15. input long user00 = 0;    //ID
16. //+------------------------------------------------------------------+
17. int OnInit()
18. {
19.     u_Interprocess Info;
20. 
21.     ResetLastError();       
22.     if (CheckPointer(control = new C_Controls(user00, "Market Replay Control", new C_Mouse(user00, "Indicator Mouse Study"))) == POINTER_INVALID)
23.             SetUserError(C_Terminal::ERR_PointerInvalid);
24.     if (_LastError != ERR_SUCCESS)
25.     {
26.             Print("Control indicator failed on initialization.");
27.             return INIT_FAILED;
28.     }       
29.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
30.     EventChartCustom(user00, C_Controls::evInit, Info.s_Infos.iPosShift, Info.df_Value, "");
31.     GlobalVariableTemp(def_GlobalVariableReplay);
32.     
33.     return INIT_SUCCEEDED;
34. }
35. //+------------------------------------------------------------------+
36. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
37. {
38.     return rates_total;
39. }
40. //+------------------------------------------------------------------+
41. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
42. {
43.     (*control).DispatchMessage(id, lparam, dparam, sparam);
44. }
45. //+------------------------------------------------------------------+
46. void OnDeinit(const int reason)
47. {
48.     switch (reason)
49.     {
50.             case REASON_TEMPLATE:
51.                     Print("Modified template. Replay/simulation system shutting down.");
52.             case REASON_PARAMETERS:
53.             case REASON_REMOVE:
54.             case REASON_CHARTCLOSE:
55.                     if (ChartSymbol(user00) != def_SymbolReplay) break;
56.                     GlobalVariableDel(def_GlobalVariableReplay);
57.                     ChartClose(user00);
58.                     break;
59.     }
60.     delete control;
61. }
62. //+------------------------------------------------------------------+

Código fonte do indicador de controle

Você pode notar que este código passou por uma lipoaspiração. Mas isto é temporário, e o motivo é que precisamos de um código mais fitness para que ele seja realmente testado de forma adequada. Basicamente você pode notar que temos muito menos chamadas e referências a classe de controle. Mas por que ?!?! O motivo é mudança na forma de organizar as coisas. Foi decidido que deveríamos ter o mínimo possível de pontos de acesso. Então a você pode notar que o código OnInit está bem diferente. Agora temos algo bem peculiar na linha 30. Neste ponto é que de fato iremos inicializar, depois claro do constructor, os dados da classe de controle. Mas para o constructor que é referenciado na linha 22, foi decidido que ele iria ser responsável apenas por inicializar os ponteiros a serem utilizados. Quem de fato iria inicializar as demais coisas na classe, seria o tratador de eventos. Por conta disto temos o que é visto na linha 30. Mas aí você pode ficar cheio de dúvidas. Então para reduzir um pouco o número delas, que tal dar uma olhada no código base da classe de controle.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\Interprocess.mqh"
005. #include "..\Auxiliar\C_DrawImage.mqh"
006. //+------------------------------------------------------------------+
007. #define def_PathBMP                "Images\\Market Replay\\Control\\"
008. #define def_ButtonPlay             def_PathBMP + "Play.bmp"
009. #define def_ButtonPause            def_PathBMP + "Pause.bmp"
010. #define def_ButtonLeft             def_PathBMP + "Left.bmp"
011. #define def_ButtonLeftBlock        def_PathBMP + "Left_Block.bmp"
012. #define def_ButtonRight            def_PathBMP + "Right.bmp"
013. #define def_ButtonRightBlock       def_PathBMP + "Right_Block.bmp"
014. #define def_ButtonPin              def_PathBMP + "Pin.bmp"
015. #resource "\\" + def_ButtonPlay
016. #resource "\\" + def_ButtonPause
017. #resource "\\" + def_ButtonLeft
018. #resource "\\" + def_ButtonLeftBlock
019. #resource "\\" + def_ButtonRight
020. #resource "\\" + def_ButtonRightBlock
021. #resource "\\" + def_ButtonPin
022. //+------------------------------------------------------------------+
023. #define def_PrefixCtrlName         "MarketReplayCTRL_"
024. #define def_PosXObjects            120
025. //+------------------------------------------------------------------+
026. #define def_SizeButtons            32
027. #define def_ColorFilter            0xFF00FF
028. //+------------------------------------------------------------------+
029. #include "..\Auxiliar\C_Terminal.mqh"
030. #include "..\Auxiliar\C_Mouse.mqh"
031. //+------------------------------------------------------------------+
032. class C_Controls : private C_Terminal
033. {
034.    protected:
035.            enum EventCustom {evInit};
036.    private :
037. //+------------------------------------------------------------------+
038.            enum eObjectControl {ePlay, eLeft, eRight, ePin, eNull};
039. //+------------------------------------------------------------------+
040.            struct st_00
041.            {
042.                    string  szBarSlider,
043.                            szBarSliderBlock;
044.                    int     Minimal;
045.            }m_Slider;
046.            struct st_01
047.            {
048.                    C_DrawImage *Btn;
049.                    bool         state;
050.                    int          x, y, w, h;
051.            }m_Section[eObjectControl::eNull];
052.            C_Mouse *m_MousePtr;
053. //+------------------------------------------------------------------+
054. inline void CreteBarSlider(int x, int size)
055.                    {
056.                            ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider = def_PrefixCtrlName + "B1", OBJ_RECTANGLE_LABEL, 0, 0, 0);
057.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
058.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Section[ePin].y + 11);
059.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
060.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
061.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
062.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
063.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
064.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
065.                            ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock = def_PrefixCtrlName + "B2", OBJ_RECTANGLE_LABEL, 0, 0, 0);
066.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
067.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Section[ePin].y + 6);
068.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
069.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
070.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
071.                    }
072. //+------------------------------------------------------------------+
073.            void SetPlay(bool state)
074.                    {
075.                            if (m_Section[ePlay].Btn == NULL)
076.                                    m_Section[ePlay].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(ePlay), def_ColorFilter, "::" + def_ButtonPlay, "::" + def_ButtonPause);
077.                            m_Section[ePlay].Btn.Paint(m_Section[ePlay].x, m_Section[ePlay].y, m_Section[ePlay].w, m_Section[ePlay].h, 20, ((m_Section[ePlay].state = state) ? 0 : 1));
078.                            ChartRedraw(GetInfoTerminal().ID);
079.                    }
080. //+------------------------------------------------------------------+
081.            void CreateCtrlSlider(void)
082.                    {
083.                            CreteBarSlider(77, 436);
084.                            m_Section[eLeft].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(eLeft), def_ColorFilter, "::" + def_ButtonLeft, "::" + def_ButtonLeftBlock);
085.                            m_Section[eRight].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(eRight), def_ColorFilter, "::" + def_ButtonRight, "::" + def_ButtonRightBlock);
086.                            m_Section[ePin].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(ePin), def_ColorFilter, "::" + def_ButtonPin);
087.                            PositionPinSlider(m_Slider.Minimal);
088.                    }
089. //+------------------------------------------------------------------+
090. inline void RemoveCtrlSlider(void)
091.                    {                       
092.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
093.                            for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++)
094.                            {
095.                                    delete m_Section[c0].Btn;
096.                                    m_Section[c0].Btn = NULL;
097.                            }
098.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName + "B");
099.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
100.                    }
101. //+------------------------------------------------------------------+
102. inline void PositionPinSlider(int p)
103.                    {
104.                            int iL, iR;
105.                            
106.                            m_Section[ePin].x = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
107.                            iL = (m_Section[ePin].x != m_Slider.Minimal ? 0 : 1);
108.                            iR = (m_Section[ePin].x < def_MaxPosSlider ? 0 : 1);
109.                            m_Section[ePin].x += def_PosXObjects;
110.                            m_Section[ePin].x += 95 - (m_Section[ePin].w / 2);
111.                            for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++)
112.                                    m_Section[c0].Btn.Paint(m_Section[c0].x, m_Section[c0].y, m_Section[c0].w, m_Section[c0].h, 20, (c0 == eLeft ? iL : (c0 == eRight ? iR : 0)));
113.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
114.                    }
115. //+------------------------------------------------------------------+
116. inline eObjectControl CheckPositionMouseClick(void)
117.                    {
118.                            C_Mouse::st_Mouse InfoMouse;
119.                            int x, y;
120.                            
121.                            InfoMouse = (*m_MousePtr).GetInfoMouse();
122.                            x = InfoMouse.Position.X_Graphics;
123.                            y = InfoMouse.Position.Y_Graphics;
124.                            for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
125.                            {
126.                                    if ((m_Section[c0].x <= x) && (m_Section[c0].y <= y) && ((m_Section[c0].x + m_Section[c0].w) >= x) && ((m_Section[c0].y + m_Section[c0].h) >= y))
127.                                            return c0;
128.                            }
129.                            
130.                            return eNull;
131.                    }
132. //+------------------------------------------------------------------+
133.    public  :
134. //+------------------------------------------------------------------+
135.            C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr)
136.                    :C_Terminal(Arg0),
137.                     m_MousePtr(MousePtr)
138.                    {
139.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
140.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
141.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName);
142.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
143.                            for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
144.                            {
145.                                    m_Section[c0].h = m_Section[c0].w = def_SizeButtons;
146.                                    m_Section[c0].y = 25;
147.                                    m_Section[c0].Btn = NULL;
148.                            }
149.                            m_Section[ePlay].x = def_PosXObjects;
150.                            m_Section[eLeft].x = m_Section[ePlay].x + 47;
151.                            m_Section[eRight].x = m_Section[ePlay].x + 511;
152.                    }
153. //+------------------------------------------------------------------+
154.            ~C_Controls()
155.                    {
156.                            delete m_MousePtr;
157.                            for (eObjectControl c0 = ePlay; c0 < eNull; c0++) delete m_Section[c0].Btn;
158.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixCtrlName);
159.                    }
160. //+------------------------------------------------------------------+
161.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
162.                    {
163.                            u_Interprocess Info;
164.                            
165.                            switch (id)
166.                            {
167.                                    case CHARTEVENT_CUSTOM + C_Controls::evInit:
168.                                            Info.df_Value = dparam;
169.                                            m_Slider.Minimal = Info.s_Infos.iPosShift;
170.                                            SetPlay(Info.s_Infos.isPlay);
171.                                            if (!Info.s_Infos.isPlay) CreateCtrlSlider();
172.                                            break;
173.                                    case CHARTEVENT_OBJECT_DELETE:
174.                                            if (StringSubstr(sparam, 0, StringLen(def_PrefixCtrlName)) == def_PrefixCtrlName)
175.                                            {
176.                                                    if (sparam == (def_PrefixCtrlName + EnumToString(ePlay)))
177.                                                    {
178.                                                            delete m_Section[ePlay].Btn;
179.                                                            m_Section[ePlay].Btn = NULL;
180.                                                            SetPlay(false);
181.                                                    }else
182.                                                    {
183.                                                            RemoveCtrlSlider();
184.                                                            CreateCtrlSlider();
185.                                                    }
186.                                            }
187.                                            break;
188.                                    case CHARTEVENT_MOUSE_MOVE:
189.                                            if ((*m_MousePtr).CheckClick(C_Mouse::eClickLeft)) switch (CheckPositionMouseClick())
190.                                            {
191.                                                    case ePlay:
192.                                                            SetPlay(!m_Section[ePlay].state);
193.                                                            if (m_Section[ePlay].state) RemoveCtrlSlider();
194.                                                            else CreateCtrlSlider();
195.                                                            break;
196.                                                    case eLeft:
197.                                                            break;
198.                                                    case eRight:
199.                                                            break;
200.                                                    case ePin:
201.                                                            break;
202.                                            }
203.                                            break;
204.                            }
205.                            ChartRedraw(GetInfoTerminal().ID);
206.                    }
207. //+------------------------------------------------------------------+
208. };
209. //+------------------------------------------------------------------+
210. #undef def_PosXObjects
211. #undef def_ButtonPlay
212. #undef def_ButtonPause
213. #undef def_ButtonLeft
214. #undef def_ButtonRight
215. #undef def_ButtonPin
216. #undef def_PrefixCtrlName
217. #undef def_PathBMP
218. //+------------------------------------------------------------------+

Código fonte da classe C_Controls

Digo código base, pois ele de fato não faz absolutamente nada de útil. Ele apenas permite e demonstra o uso da classe de desenho que foi vista mais acima e uma interação muito simples com o indicador de mouse. Então vamos ver o que temos aqui. Na linha 26 definimos o tamanho que os botões de interação irão ter. Já na linha 27, informamos qual será a cor utilizada, para indicar transparência total da área da imagem. Você pode ver esta cor nas imagens em anexo que serão embutidas no executável em forma de recursos.

Agora atenção, na linha 38 indicamos quais serão os controles básicos, e entre as linhas 46 a 51 temos uma estrutura que irá ficar contida em uma matriz que irá acomodar os controles. Atenção a isto, pois perceber este tipo de construção será importante para os próximos passos.

Se você observar o código da classe de controle, irá notar que não estamos mais usando todas aquelas chamadas que existiam antes. De fato, o código passou por uma remodelagem bastante extensa. Neste artigo não irei entrar em todos os detalhes, já que ele ainda não está em seu estágio mais avançado, nos permitindo um controle total. Mas dado o que foi implementado. Vou dar uma explicada no procedimento SetPlay, este se encontra na linha 73. Não tenho certeza se este será o nome final deste procedimento, mas por hora será este.

Neste procedimento, iremos verificar na linha 75, se o ponteiro para o botão de play e pause, já foi criado. Caso isto não tenha sido feito, na linha 76 iremos executar o constructor da classe de desenho. Assim informamos quais serão as imagens a serem utilizadas. Da mesma forma como seria feito se fossemos usar a função ObjectCreate da biblioteca do MQL5. Então na linha 77, iremos plotar a imagem na posição que estará sendo indicada no controle do botão. e dizemos qual será a imagem a ser plotada. Por fim na linha 78, pedimos para que o gráfico seja atualizado, e assim a imagem venha a ser plotada.

Não irei explicar neste momento, as demais funções já que isto será explicado depois. Mas quero que voltem a atenção a linha 188. Observem que nesta linha iremos interceptar os eventos de mouse, que o MetaTrader 5 repassar para nós. Irei explicar rapidamente o que está acontecendo, apenas para que você entenda o básico, depois quando o código estiver mais avançado irei explicar em detalhes o que de fato acontece aqui.

Mas quando o MetaTrader 5 nos envia um evento de mouse, iremos perguntar ao indicador de mouse, se ocorreu um clique com o botão esquerdo. Isto apenas irá ser confirmado, se o indicador de mouse não estiver em modo de estudo. Se o clique de fato aconteceu, iremos fazer com que a classe de controle verifique quem foi clicado. Aqui mora uma falha que depois iremos resolver. Mas quando foi verificado que o clique foi no botão de play / pause iremos executar o código presente entre as linhas 191 e 195. De forma a termos uma noção da interação que esteja acontecendo entre o usuário e todo o sistema.

No vídeo logo abaixo você pode ver como as coisas estão se mostrando. Note que ainda não temos de fato algo funcional. Mas a ideia aqui era tentar ajustar as coisas de forma a ter justamente esta interação do indicador de mouse e do indicador de controle.




Conclusão

Neste artigo, mostrei como você faz para modificar o sistema que se encontra implementado, de maneira que podemos tornar as coisas consideravelmente mais agradáveis e úteis. Fazendo com que no longo prazo, comecemos a programar cada vez menos e tenhamos um sistema cada vez mais estável e confiável. Mas ao mesmo tempo, mostrei que muitas das vezes temos que nos aprofundar no uso do MQL5, de forma a conseguir de fato produzir algo que tenha um resultado melhor.

Apesar de não colocar neste artigo os executáveis como anexo, isto por eles não estarem funcionais, você encontrará as imagens que deverão substituir as antigas. Caso você queira obter o mesmo sistema que estou desenvolvendo. Ao mesmo tempo mantendo o mesmo código que o mostrado nos artigos. Mas sinta-se livre em modificar ou ajustar as coisas ao que você de fato precisa.

No próximo artigo, vamos continuar a desenvolver este indicador de controle. Pois a coisa agora irá começar a se expandir de forma geométrica.

Arquivos anexados |
Anexo.zip (420.65 KB)
Redes neurais de maneira fácil (Parte 69): restrição de política comportamental com base na densidade de dados off-line (SPOT) Redes neurais de maneira fácil (Parte 69): restrição de política comportamental com base na densidade de dados off-line (SPOT)
No aprendizado off-line, utilizamos um conjunto de dados fixo, e isso não abrange toda a variedade do ambiente. Durante o processo de treinamento, nosso Agente pode gerar ações fora desse conjunto. Sem feedback do ambiente, a precisão dessas ações é duvidosa. Manter a política do Agente dentro do conjunto de treinamento se torna importante para confiar nos resultados. Vamos falar mais sobre isso aqui neste artigo.
Algoritmos de otimização populacional: algoritmos de estratégias evolutivas (Evolution Strategies, (μ,λ)-ES e (μ+λ)-ES) Algoritmos de otimização populacional: algoritmos de estratégias evolutivas (Evolution Strategies, (μ,λ)-ES e (μ+λ)-ES)
Neste artigo, vamos falar sobre um grupo de algoritmos de otimização conhecidos como "Estratégias Evolutivas" (Evolution Strategies ou ES). Eles são alguns dos primeiros algoritmos que usam princípios de evolução para encontrar soluções ótimas. Vamos mostrar as mudanças feitas nas versões clássicas das ES, além de revisar a função de teste e a metodologia de avaliação dos algoritmos.
Redes neurais de maneira fácil (Parte 70): melhorando a política usando operadores de forma fechada (CFPI) Redes neurais de maneira fácil (Parte 70): melhorando a política usando operadores de forma fechada (CFPI)
Neste artigo, propomos explorar um algoritmo que utiliza operadores de melhoria de política de forma fechada para otimizar as ações do Agente em um ambiente off-line.
Padrões de projeto no MQL5 (Parte 4): Padrões comportamentais 2 Padrões de projeto no MQL5 (Parte 4): Padrões comportamentais 2
Com este artigo concluímos a série sobre padrões de projeto na área de software. Já mencionei que existem três tipos de padrões de projeto: criacionais, estruturais e comportamentais. Finalizaremos os padrões comportamentais restantes, que ajudarão a definir a maneira de interação entre objetos, de modo a tornar nosso código mais limpo.