preview
Desenvolvendo um sistema de Replay (Parte 54): O nascimento do primeiro módulo

Desenvolvendo um sistema de Replay (Parte 54): O nascimento do primeiro módulo

MetaTrader 5Exemplos | 26 junho 2024, 10:19
65 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Desenvolvendo um sistema de Replay (Parte 53): Complicando as coisas (V), expliquei alguns dos conceitos que deste momento em diante irá passar a fazer parte do que iremos de fato programar e usar em termos de MQL5 e plataforma MetaTrader 5. Sei que muito daquele conceito, é novo para grande parte daqueles que por ventura venham a ler estes meus artigos. Mas igualmente sei, que tais conceitos, são de conhecimento de todos com experiência em programação de sistemas, como por exemplo: Programadores Windows.

Então para quem deseja de fato se aprofundar, a fim de saber por que, e de que forma o que será mostrado funciona, aconselho você a estudar um pouco de como fazer programação Windows, para assim saber como de fato a troca de mensagens entre programas se dá. Pois aqui, nos artigos isto iria me fazer desviar muito o foco do que de fato desejo mostrar como projetar, e de como trabalhar no MQL5 e no MetaTrader 5 de uma forma mais avançada.

Você não encontrará dificuldades para encontrar programas que usam estas trocas de mensagens, a fim de conseguir se comunicar no ambiente Windows, porém para que a compreensão seja adequada, você deverá ter um conhecimento prévio, e uma base bem sólida e consolidada em termos de programação C. Se você não tem tal conhecimento, meu conselho é: Comece aprendendo programação C, depois estude como a troca de mensagens se dá entre programas na plataforma Windows. Assim você conseguirá gerar uma base ampla e sólida para entender o que estarei mostrando a partir de agora.


Tornando as coisas possíveis

Se você prestou atenção ao que foi mostrado no artigo anterior, deve ter notado que durante um longo período de tempo, estive tentando fazer algo. Mas, no entanto, apesar de as coisas estarem funcionando, parcialmente, elas não poderiam coexistir em um sistema mais amplo. Não da forma como está sendo desenvolvido.

Talvez a maior das falhas, que cometi, foi justamente o fato de ignorar por várias semanas, o fato de que nossas aplicações, respondem a eventos da plataforma MetaTrader 5. Mas a falha não foi bem esta, e sim o fato de que eu não estava pensando no MetaTrader 5, em termos de uma plataforma, e sim como se ele fosse apenas um programa, onde outros processos iriam rodar dentro do mesmo.

Tal falha na forma de ver o MetaTrader 5, se deve ao fato de que outras plataformas não nos dá a mesma flexibilidade que temos no MetaTrader 5, e isto me custou, algum atraso em termos de velocidade para desenvolver as aplicações de forma mais avançada. No entanto, uma vez que este sistema de replay / simulador, se mostrou mais adequado de ser desenvolvido de uma forma mais modular, diferente do que muitos normalmente fazem, ou do que a grande parte das aplicações que rodam no MetaTrader 5, necessita. Acabei notando alguns problemas. Mas não se tratava exatamente de problemas, e sim de algo que eu estava ignorando.

Você pode ver nos vídeos do artigo anterior, que de fato, o MetaTrader 5, se presta a nos oferecer muito mais do que muitos de fato tem explorado. Mas agora vamos tornar o que antes era apenas um desejo, em algo realizável. Vamos passar a programar menos e construir mais. Irei a partir deste artigo, começar a explorar a troca de mensagens, a fim de conseguir fazer o MetaTrader 5, trabalhar mais para mim, enquanto me preocupo apenas em manter a aplicação funcionando de maneira harmoniosa com outras presentes no gráfico.

E a primeira coisa que iremos fazer, será adequar o indicador de mouse, a fim de que possamos começar com ele esta nova etapa, no desenvolvimento de aplicações para o MetaTrader 5.

Já que o código irá sofrer algumas mudanças, um tanto quanto radicais. Será preciso mudar muitas partes do mesmo. No entanto, se você vem acompanhando a sequência, não irá ter grandes dificuldades em fazer tais mudanças.

Então logo de cara, iremos criar um arquivo, que será comum em todas as aplicações que se seguirem a partir de agora. Já que ela irá permitir que tenhamos um direcionamento das mensagens a serem tratadas por todo e qualquer código que iremos criar, deste momento em diante. Isto irá nos dar um certo controle sobre o que irá acontecer, quando duas aplicações, que tem um tratador de mensagens estiver no gráfico. Assim cada aplicação irá conseguir tratar a mensagem de maneira adequada e correta.

O conteúdo inicial deste arquivo pode ser visto logo abaixo. Ele deverá ser salvo com o nome de Defines.mqh. E o local onde ele deverá estar será visto em breve. Então se você não sabe absolutamente nada de programação. Me desculpe, mas deste momento em diante, você não conseguirá mais ter acesso ao que irei de fato fazer. De agora em diante, existirá uma barreira que não irá lhe permitir continuar. Se você deseja de fato usar o que irei mostrar, será preciso aprender o mínimo sobre programação.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_VERSION_DEBUG
05. //+------------------------------------------------------------------+
06. #ifdef def_VERSION_DEBUG
07.     #define macro_DEBUG_MODE(A) \
08.                             Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A));
09. #else
10.     #define macro_DEBUG_MODE(A)
11. #endif
12. //+------------------------------------------------------------------+
13. #define def_SymbolReplay            "RePlay"
14. #define def_MaxPosSlider            400
15. //+------------------------------------------------------------------+
16. union uCast_Double
17. {
18.     double   dValue;
19.     long     _long;                                  // 1 Information
20.     datetime _datetime;                              // 1 Information
21.     int      _int[sizeof(double) / sizeof(int)];     // 2 Informations
22.     char     _char[sizeof(double) / sizeof(char)];   // 8 Informations
23. };
24. //+------------------------------------------------------------------+
25. enum EnumEvents    {
26.                     evHideMouse,        //Hide mouse price line
27.                     evShowMouse,        //Show mouse price line
28.                     evHideBarTime,      //Hide bar time
29.                     evShowBarTime,      //Show bar time
30.                     evHideDailyVar,     //Hide daily variation
31.                     evShowDailyVar,     //Show daily variation
32.                     evHidePriceVar,     //Hide instantaneous variation
33.                     evShowPriceVar,     //Show instantaneous variation
34.                     evSetServerTime,    //Replay/simulation system timer
35.                     evCtrlReplayInit    //Initialize replay control
36.                    };
37. //+------------------------------------------------------------------+

Código fonte do arquivo Defines.mqh

Na linha 4 deste arquivo, você pode notar que definimos algo. Desta forma, se você desejar debugar, em tempo de execução o código, adicione a macro_DEBUG_MODE no seu código, passando a variável a ser debugada como parâmetro, assim será possível colocar e remover rapidamente um sistema de modo a poder analisar em tempo de execução o que está acontecendo. Bastando para isto, verificar se a linha 4, está ou não sendo usada pelo compilador.

Na linha 13 temos a definição do nome a ser usado no nosso símbolo customizado, a fim de ser utilizado pelo serviço de replay / simulador, como está sendo feito deste o início desta sequência de artigos. Já na linha 14, declaramos algo que será de fato usado apenas pelo indicador de controle.

Tanto estas duas últimas linhas, quando o conteúdo que está presente, entre as linhas 16 e 23, já faziam parte do código que vinha sendo escrito. Mas por conta que agora o arquivo de cabeçalho InterProcess.mqh irá deixar de existir, foi necessário transferir tais informações para este arquivo de definição.

Mas o que de fato nos interessa neste arquivo, se inicia na linha 25. Neste ponto declaramos uma enumeração, que irá crescer conforme novos eventos vão sendo necessários, e sendo adicionados. Aqui mora um pequeno perigo, mas não tão sério se você tomar o devido cuidado, que é sempre adicionar uma enumeração no final da lista. Se isto for feito, os códigos mais antigos não precisaram ser compilados novamente, mas se você adicionar algo no meio da lista, deverá compilar novamente todos os códigos antigos. Assim você não terá problemas.

Note que em cada uma das linhas estamos definindo um valor, e em cada uma estou fazendo um breve comentário sobre do que se trata o evento.

Você irá notar, conforme os códigos serão mostrados, onde e como cada um destes eventos irá ser tratado, mas o principal, será algo que somente poderá ser visto no código dos mesmos. Então se você pretende usar parte do que estou mostrando, é bom ficar atento aos tratadores de mensagens.

E com isto começamos de fato a tornar todo o sistema algo totalmente modular. Deste momento em diante, preste bastante atenção ao código, e a que tipo de coisa estará presente no gráfico. Chegou a hora de usar o MetaTrader 5, como um verdadeiro profissional.

Então como você já deve estar imaginando. Sim foi preciso mudar novamente o código das classes do indicador de mouse, mas tais mudanças são necessárias justamente para promover o correto uso, e implementação do que iremos de fato fazer. Mas antes de mexer no código do indicador de mouse, será preciso fazer uma pequena mudança no código da classe C_Terminal. Então no arquivo de cabeçalho C_Terminal.mqh, faça o que está sendo mostrado no fragmento abaixo.
01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "Macros.mqh"
05. #include "..\Defines.mqh"
06. #include "Interprocess.mqh"
07. //+------------------------------------------------------------------+
08. class C_Terminal
09. {
10. //+------------------------------------------------------------------+
11.     protected:
12.             enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance};
13. //+------------------------------------------------------------------+
14.             struct st_Terminal

Fragmento de código do arquivo C_Terminal.mqh

Observe com atenção a linha 5, ela indica exatamente qual a localização do arquivo de cabeçalho Defines.mqh, visto a pouco, isto em relação ao arquivo de cabeçalho C_Terminal.mqh. Mas não é somente isto que quero chamar a sua atenção. Quero que note, o fato de que a linha 6 foi removida do código, ou seja, você já pode apagar o arquivo de cabeçalho InterProcess.mqh do projeto, pois ele já não será mais utilizado.

Já que esta mudança é bastante simples, e que não aconteceu nenhuma outra mudança no código do arquivo C_Terminal.mqh, não vejo necessidade de replicar todo ele. Mas mesmo não tendo sofrido grandes mudanças, aconteceu uma que é preciso ser mencionada, caso contrário, você irá ter problemas ao tentar compilar os códigos que serão visto.

Observe que na linha 12, temos uma enumeração, a mesma recebeu, um novo valor. Este se destina a ser utilizado, durante o teste a fim de verificar se já existe a ocorrência de um mesmo indicador no gráfico. Desta forma então é necessário, uma outra pequena mudança, neste mesmo arquivo de cabeçalho C_Terminal.mqh. Tal mudança a ser feita pode ser observada no fragmento logo abaixo.

157. //+------------------------------------------------------------------+
158.            bool IndicatorCheckPass(const string szShortName)
159.                    {
160.                            string szTmp = szShortName + "_TMP";
161.                            
162.                            if (_LastError != ERR_SUCCESS) return false;
163.                            IndicatorSetString(INDICATOR_SHORTNAME, szTmp);
164.                            if (ChartWindowFind(m_Infos.ID, szShortName) != -1)
165.                            {
166.                                    ChartIndicatorDelete(m_Infos.ID, 0, szTmp);
167.                                    Print("Only one instance is allowed...");
168.                                    SetUserError(C_Terminal::ERR_NoMoreInstance);
169.                                    
170.                                    return false;
171.                            }
172.                            IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
173.                            ResetLastError();
174.    
175.                            return true;
176.                    }
177. //+------------------------------------------------------------------+

Fragmento a ser modificado no arquivo C_Terminal.mqh

Você deverá modificar a função, original presente no arquivo C_Terminal.mqh, pelo código que é visto no fragmento 2. Desta forma o sistema irá durante o teste fazer com que a constante _LastError seja corretamente ajustada a fim de indicar que ocorreu um erro, e o motivo foi a existência de uma outra instância do mesmo indicador no gráfico. Fora estas duas simples modificações que foram feitas, nenhuma outra de fato aconteceu, então podemos dar segmento ao nosso trabalho, e começar a trabalhar nas classes do indicador de mouse.

Abaixo, você pode ver na integra, todo o código do arquivo de cabeçalho C_Mouse.mqh. Já que alguns podem sentir um pouco de dificuldades em compreender adequadamente o código, irei dar uma breve explicação do que está acontecendo. Assim você conseguirá acompanhar um pouco melhor como as coisas começam a realmente se tornar modulares.

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

Código fonte do arquivo C_Mouse.mqh

Note que diferente do que acontecia antes, agora temos apenas e somente a inclusão do arquivo C_Terminal.mqh, como sendo necessária. Mas mesmo assim você deverá prestar atenção a alguns outros detalhes, que foram modificados nesta classe vista acima.

Atenção ao fato de agora, na linha 30, termos uma nova variável. Esta somente terá algum valor para nós, no momento em que o serviço de replay / simulador estiver de fato em uso. O que esta variável faz, é justamente nos dar acesso a um valor que será informado pelo serviço de replay / simulador, que antes era feito via variável global de terminal. Apesar desta variável estar sendo declarada aqui, na linha 30, ela não é de fato usada aqui, e sim em outro local. Mas por conta de uma outra questão que irei explicar depois, foi necessário adiciona-la justamente aqui, na classe C_Mouse.

Você irá notar que grande parte do código continua idêntico, ao que era antes, até que na linha 168, temos algo diferente. Agora iremos usar seis posições do buffer. A tal sexta posição, será ocupada justamente pela variável que está declarada na linha 30. Então na linha 181, temos o preenchimento do valor, quando for necessário fazer a leitura, e saber qual o valor que se encontra presente nesta variável. Aqui preciso abrir um parêntese e explicar algo. O único procedimento que irá de fato usar, até o devido momento, o conteúdo da variável declarada na linha 30, será o serviço de replay / simulador, e o indicador de mouse. Este último irá usar este valor, a fim de informar ao usuário quanto tempo falta para a próxima barra vim a ser aberta. No entanto, caso você deseje não fazer uso de tal coisa, poderá remover isto no indicador de mouse, ou mesmo no serviço de replay / simulador. Não haverá nenhum tipo de problema ao se fazer isto. Mas se deseja usar esta informação, precisa saber, que sem fazer uso de uma variável global de terminal, ou de outro meio, para armazenar o valor de tempo do serviço de replay / simulação, este mesmo serviço, não saberá quando será necessário atualizar novamente este dado.

Por conta disto é que colocamos, o valor no buffer do indicador de mouse. Mas aí você pensa: Espera um pouco. Este valor não será removido e zerado no momento que o indicador for recolocado no gráfico, pelo MetaTrader 5, por conta que o usuário modificou o tempo gráfico ?! Então guardar ele no buffer do indicador não faz nenhum sentido. Na verdade, é justamente isto que queremos. No momento que o serviço detectar que o valor presente no buffer não é mais o que deveria ser, iremos mandar um evento para o indicador de mouse, a fim de atualizar novamente este mesmo valor. Desta maneira iremos facilmente manter as coisas como seria o esperado.

Ou seja, o simples fato de que o MetaTrader 5, ao forçar o indicador zerar o buffer, irá fazer com que o serviço de replay / simulação, perceba que o usuário fez alguma mudança, e que esta precisa ser reavaliada pelo serviço de replay / simulação. Assim nossa aplicação irá sem esforço algum perceber que algo aconteceu, pois o MetaTrader 5 irá fazer todo o trabalho pesado para nós.

Mas a questão de fato fica ainda mais interessante, é quando olhamos o código, a partir da linha 189, que é justamente onde está o tratador de mensagens da classe C_Mouse.

Dentro deste procedimento de tratamento de mensagens, temos 3, das primeiras mensagens que nosso sistema modular irá começar a tratar. Então preste atenção no como isto estará sendo feito. Em primeiro lugar, para que o tratamento de mensagens seja de fato feito, pelo indicador, e não por algum outro código que esteja usando a classe. Fazemos um teste, a fim de verificar se estamos nos referindo ao indicador, ou um tradutor. Tal testes é feito na linha 194. Se este teste passar, iremos considerar que de fato estamos tratando do indicador, por isto precisamos que exista apenas um único indicador de mouse no gráfico, isto para evitar possíveis conflitos de interesse. Já expliquei sobre isto em outros artigos, então, podemos apenas continuar a fazer o que tem que ser realmente feito.

Então na linha 199, iremos tratar de um evento, no qual o indicador de mouse, deverá ocultar a linha usada como sendo a linha de preço.

Já na linha 202, iremos tratar do evento, no qual é dito ao indicador de mouse, que a linha de preço deverá ser novamente mostrada no gráfico. Desta forma, qualquer aplicação poderá dizer ao indicador de mouse, quando mostrar ou ocultar a linha de preço que ele utiliza.

Em ambos eventos não é necessário informar nenhum tipo de parâmetro extra, então tudo que alguma aplicação que deseja ocultar ou mostrar a linha do mouse, precisa de fato fazer, é gerar um evento customizado, com os valores indicados. E dirigir este evento ao gráfico onde o indicador de mouse se encontra presente. Irei explicar mais sobre isto depois. Por hora entenda assim, se em algum momento você estiver com o indicador de mouse no gráfico, e deseja ocultar a linha de mouse, deverá gerar um evento customizado com o valor correto, assim irá conseguir ocultar ou mostrar a linha de mouse. Tal evento pode ser disparado por um Expert Advisor por exemplo.

O terceiro evento, que também será implementado aqui, é visto na linha 205. Neste iremos indicar o valor que será colocado como tempo do serviço, ou seja, quem de fato irá fazer isto, deverá ser o serviço de replay / simulador. Já que este será o único que de fato irá tirar algum proveito em gerar tal evento. Mas note um detalhe. Quando este evento for lançado, ele deverá indicar um valor de tempo. Tal valor deverá estar no parâmetro dparam, que é um valor double.

Novamente, é preciso que você passe a ver as coisas de maneira mais ampla. Um valor double é composto de 8 bytes. A mesma coisa acontece com um valor datetime, que também é composto de 8 bytes. Desta forma fazemos uma conversão de tipos a fim de que o compilador consiga compreender o que estamos fazendo. Mas para o processador, tudo que estamos fazendo é colocando 8 bytes em uma variável. O conteúdo de tais bytes pouco importa.

Entender isto é importante, pois existem situações que precisamos passar cadeias inteiras de valores, as vezes estruturas inteiras, e como no MetaTrader 5, ou melhor dizendo, no MQL5, não podemos usar ponteiros como no C / C++, precisaremos de algum tipo de artimanha a fim de conseguir passar tais dados entre as nossas aplicações que estão rodando no MetaTrader 5.

Muito bem, então a primeira parte do trabalho, já está feita. Mas ainda vamos melhorar as coisas. Se você de fato prestou atenção, deve ter notado que existem alguns outros eventos relacionados ao indicador de mouse. Só que estes eventos não estão no arquivo de cabeçalho C_Mouse.mqh, eles estão de fato é no arquivo C_Study.mqh. Então para ver tais eventos, e assim entender o que estará acontecendo, vamos ver o dito código. Este pode ser visto na integra logo abaixo:

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.                    bool           bvT, bvD, bvP;
024.            }m_Info;
025. //+------------------------------------------------------------------+
026.            const datetime GetBarTime(void)
027.                    {
028.                            datetime dt;
029.                            int i0 = PeriodSeconds();
030.                            
031.                            if (m_Info.Status == eInReplay)
032.                            {
033.                                    if ((dt = GetInfoMouse().TimeDevice) == ULONG_MAX) return ULONG_MAX;
034.                            }else dt = TimeCurrent();
035.                            if (m_Info.Rate.time <= dt)
036.                                    m_Info.Rate.time = (datetime)(((ulong) dt / i0) * i0) + i0;
037. 
038.                            return m_Info.Rate.time - dt;
039.                    }
040. //+------------------------------------------------------------------+
041.            void Draw(void)
042.                    {
043.                            double v1;
044.                            
045.                            if (m_Info.bvT)
046.                            {
047.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 18);
048.                                    ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn1, OBJPROP_TEXT, m_Info.szInfo);
049.                            }
050.                            if (m_Info.bvD)
051.                            {
052.                                    v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / GetInfoMouse().Position.Price) * 100.0), 2);
053.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1);
054.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
055.                                    ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
056.                            }
057.                            if (m_Info.bvP)
058.                            {
059.                                    v1 = NormalizeDouble(100.0 - ((m_Info.Rate.close / iClose(GetInfoTerminal().szSymbol, PERIOD_D1, 0)) * 100.0), 2);
060.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y_Adjusted - 1);
061.                                    ObjectSetInteger(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
062.                                    ObjectSetString(GetInfoTerminal().ID, def_ExpansionBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
063.                            }
064.                    }
065. //+------------------------------------------------------------------+
066. inline void CreateObjInfo(EnumEvents arg)
067.                    {
068.                            switch (arg)
069.                            {
070.                                    case evShowBarTime:
071.                                            C_Mouse::CreateObjToStudy(2, 110, def_ExpansionBtn1, clrPaleTurquoise);
072.                                            m_Info.bvT = true;
073.                                            break;
074.                                    case evShowDailyVar:
075.                                            C_Mouse::CreateObjToStudy(2, 53, def_ExpansionBtn2);
076.                                            m_Info.bvD = true;
077.                                            break;
078.                                    case evShowPriceVar:
079.                                            C_Mouse::CreateObjToStudy(58, 53, def_ExpansionBtn3);
080.                                            m_Info.bvP = true;
081.                                            break;
082.                            }
083.                    }
084. //+------------------------------------------------------------------+
085. inline void RemoveObjInfo(EnumEvents arg)
086.                    {
087.                            string sz;
088.                            
089.                            switch (arg)
090.                            {
091.                                    case evHideBarTime:
092.                                            sz = def_ExpansionBtn1;
093.                                            m_Info.bvT = false;
094.                                            break;
095.                                    case evHideDailyVar:
096.                                            sz = def_ExpansionBtn2;
097.                                            m_Info.bvD      = false;
098.                                            break;
099.                                    case evHidePriceVar:
100.                                            sz = def_ExpansionBtn3;
101.                                            m_Info.bvP = false;
102.                                            break;
103.                            }
104.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
105.                            ObjectDelete(GetInfoTerminal().ID, sz);
106.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
107.                    }
108. //+------------------------------------------------------------------+
109.    public  :
110. //+------------------------------------------------------------------+
111.            C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
112.                    :C_Mouse(IdParam, szShortName, corH, corP, corN)
113.                    {
114.                            if (_LastError != ERR_SUCCESS) return;
115.                            ZeroMemory(m_Info);
116.                            m_Info.Status = eCloseMarket;
117.                            m_Info.Rate.close = iClose(GetInfoTerminal().szSymbol, PERIOD_D1, ((GetInfoTerminal().szSymbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(GetInfoTerminal().szSymbol, PERIOD_D1, 0))) ? 0 : 1));
118.                            m_Info.corP = corP;
119.                            m_Info.corN = corN;
120.                            CreateObjInfo(evShowBarTime);
121.                            CreateObjInfo(evShowDailyVar);
122.                            CreateObjInfo(evShowPriceVar);
123.                    }
124. //+------------------------------------------------------------------+
125.            void Update(const eStatusMarket arg)
126.                    {
127.                            datetime dt;
128.                            
129.                            switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
130.                            {
131.                                    case eCloseMarket :
132.                                            m_Info.szInfo = "Closed Market";
133.                                            break;
134.                                    case eInReplay    :
135.                                    case eInTrading   :
136.                                            if ((dt = GetBarTime()) < ULONG_MAX)
137.                                            {
138.                                                    m_Info.szInfo = TimeToString(dt, TIME_SECONDS);
139.                                                    break;
140.                                            }
141.                                    case eAuction     :
142.                                            m_Info.szInfo = "Auction";
143.                                            break;
144.                                    default           :
145.                                            m_Info.szInfo = "ERROR";
146.                            }
147.                            Draw();
148.                    }
149. //+------------------------------------------------------------------+
150. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
151.                    {
152.                            C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
153.                            switch (id)
154.                            {
155.                                    case CHARTEVENT_CUSTOM + evHideBarTime:
156.                                            RemoveObjInfo(evHideBarTime);
157.                                            break;
158.                                    case CHARTEVENT_CUSTOM + evShowBarTime:
159.                                            CreateObjInfo(evShowBarTime);
160.                                            break;
161.                                    case CHARTEVENT_CUSTOM + evHideDailyVar:
162.                                            RemoveObjInfo(evHideDailyVar);
163.                                            break;
164.                                    case CHARTEVENT_CUSTOM + evShowDailyVar:
165.                                            CreateObjInfo(evShowDailyVar);
166.                                            break;
167.                                    case CHARTEVENT_CUSTOM + evHidePriceVar:
168.                                            RemoveObjInfo(evHidePriceVar);
169.                                            break;
170.                                    case CHARTEVENT_CUSTOM + evShowPriceVar:
171.                                            CreateObjInfo(evShowPriceVar);
172.                                            break;
173.                                    case CHARTEVENT_MOUSE_MOVE:
174.                                            Draw();
175.                                            break;
176.                            }
177.                            ChartRedraw(GetInfoTerminal().ID);
178.                    }
179. //+------------------------------------------------------------------+
180. };
181. //+------------------------------------------------------------------+
182. #undef def_ExpansionBtn3
183. #undef def_ExpansionBtn2
184. #undef def_ExpansionBtn1
185. #undef def_ExpansionPrefix
186. #undef def_MousePrefixName
187. //+------------------------------------------------------------------+

Código fonte do arquivo C_Study.mqh

Diferente do que foi em C_Mouse.mqh, aqui temos muitas diferenças. A começar pelo fato de termos mais algumas novas variáveis, testes e coisas sendo feitas. Mas como grande parte destas coisas são simples, podemos focar em apenas algumas mais exóticas. Entre estas, está a linha 33, onde agora você pode notar que não fazemos mais fazendo uso de uma função a fim de acessar uma variável global de terminal. O que estamos fazendo agora, é perguntar, ao indicador de mouse, algo que antes era buscado em uma variável global de terminal. Este tipo de coisa é o que estamos procurando de fato implementar, ou seja, queremos fazer as coisas sem precisar usar algo que o usuário possa manipular, mas sem precisar de fato recorrer a programação externa. A ideia é fazer tudo em MQL5, puro.

Talvez não fique muito claro, apenas observando o código, de que estamos implementando algo, muito mais customizável do que existia antes. Isto tem um proposito, tornar o nosso indicador de mouse, um tipo de padrão a ser usado em outras aplicações dentro da plataforma. Mas com um detalhe. Você como programador, poderá enviar mensagens para o indicador de mouse, a fim de desligar ou ligar algo que já esteja definido dentro dele.

Para fazer isto, é preciso isolar algumas partes do código, de modo que quando um evento pedir para ligar, ou desligar algo, o objeto no qual estaremos nos referindo, sofra as mudanças que queremos de fato fazer. No caso estamos falando de algo simples, como fazer algo aparecer ou não no gráfico. Mas poderia ser algo ainda mais complicado. Isto não importa. O grau de liberdade que estamos começando a criar aqui, torna possível inúmeras coisas, e todas elas serão sempre pequenas modificações do que já estará pronto, ou seja, o nível de segurança e confiabilidade da aplicação irá se manter sempre a mais alta quanto for possível ser implementada.

Muito do código que você vê no arquivo C_Study.mqh, serve justamente para permitir isto. Mas o que realmente vem a nos interessar neste momento, é o que acontece a partir da linha 150. Ali iremos fazer a implementação do que será tratado neste arquivo de cabeçalho. Observe que as partes pendentes que não nos interessa tratar aqui, são repassadas para dentro da classe C_Mouse, a fim de que os eventos possam ser tratados lá. Isto pode ser observado na linha 152.

Agora preste atenção a uma coisa. Cada um dos eventos customizados que podem ser vistos neste tratador irá ligar ou desligar algo no indicador de mouse, fazendo com que ele fique ligeiramente diferente, mas isto será feito em tempo de execução, sem a necessidade de precisar de fato compilar novamente o código, ou forçar o usuário a configurar uma lista muito grande de opções. Atenção a este fato. Então você pode simplesmente criar pequenos arquivos de script, para assim enviar um evento customizado para o indicador de mouse, a fim de que a aparência do mesmo seja modificada durante o seu uso.

Se você tiver a devida criatividade, já estará pensando e planejando diversas coisas apenas observando este código, e o vendo funcionar. Mas quero que você tenha um pouco de calma, devido ao fato de que no momento atual, ainda não está totalmente claro, qual será a direção que o sistema de replay / simulador irá de fato tomar. Isto por que ao tornar as coisas módulos, abre-se um grande leque de possibilidades. E a forma como o sistema pode vim a crescer, faz com que tomemos alguns cuidados, ao começar a pensar em novas possibilidades. Mas no momento, estou focado em fazer com que o sistema comece a funcionar, sem a necessidade de usar as variáveis globais de terminal. Mas ao mesmo tempo, que ele tenha as mesmas qualidades que eram vistas quando os módulos começaram a ser criados.

Muito bem. Com tudo isto já tendo sido mostrado, chegamos finalmente no código que de fato criar o indicador de mouse. Este pode ser visto na integra 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.54"
007. #property icon "/Images/Market Replay/Icons/Indicators.ico"
008. #property link "https://www.mql5.com/pt/articles/11971"
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 - 6;
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.    m_Buff[m_posBuff + 5] = (double)(*Study).GetInfoMouse().TimeDevice;
101. }
102. //+------------------------------------------------------------------+

Código fonte do indicador de Mouse

Note que praticamente não existe nenhuma grande mudança no código. Mas apenas uma nova adição que foi feita, e que pode ser vista na linha 100. Esta, é justamente a linha que coloca um dado no buffer do indicador. Mais uma vez, este dado somente tem utilidade, neste primeiro momento para o serviço de replay / simulador. Não tendo nenhuma outra serventia. Bem, pelo menos não uma que eu consiga de fato notar.


Conclusão

Para que você de fato consiga entender o que está acontecendo aqui, irei deixar no anexo, o executável do indicador, junto com alguns scripts, para serem usados a fim de modificar o indicador quando este estiver no gráfico. Mas para aqueles que não querem, ou pretendem rodar um programa já compilado em sua plataforma. E de fato entendo a sua desconfiança. Você pode ver no vídeo logo no final deste artigo, o que acontece quando rodamos os scripts.

Percebam, que é apenas uma pequena demonstração do que podemos de fato fazer. Muitos com uma visão mais míope, irão dizer que isto é bobagem, que poderia ser feito de outra forma. Mas estes irei ignorar. Prefiro mesmo, é ver o brilho nos olhos de que pensa nas possibilidades do que estou mostrando. No que pode vim a ser este sistema modular e como as coisas irão se desenvolver de agora em diante. É justamente para estes que estou fazendo estes artigos. Para provar, que muitos apenas estão arranhando a superfície do que realmente podemos fazer no MQL5, e que o MetaTrader 5, é de fato uma grande plataforma. E nas mãos certas, tem possibilidade de fazer coisas incríveis.

No entanto, mais uma vez, peço desculpas a quem de fato não tem conhecimento em programação. Lamento, mas sem o devido conhecimento, sobre o que está sendo mostrado aqui, esta aplicação do serviço de replay / simulação, se torna algo perigoso de você vim a ter acesso ao código fonte. Mas prometo não lhe deixar totalmente na mão. Prometo sempre que a versão de algum modulo estiver concluída e estável. Disponibilizar o executável, aqui nos artigos. No entanto se você deseja de fato compilar o código fonte, este estará sempre disponível no artigo. Porém, sei que sem o conhecimento adequado, você não irá conseguir criar a estrutura de diretórios, necessária para de fato compilar o sistema.

Esta é justamente a minha intenção, já que por experiência, vi que em artigos passados, pessoas estavam tentando compilar algo, sem de fato saber do que se tratava. E este tipo de coisa além de perigosa, é arriscada. Você não deve de fato fazer uso de algo em entender para que aquilo serve.


Vídeo 01 - Demonstração de funcionamento do módulo

Arquivos anexados |
Anexo.zip (420.65 KB)
EA de grid-hedge modificado em MQL5 (Parte II): Criando um EA de grade simples EA de grid-hedge modificado em MQL5 (Parte II): Criando um EA de grade simples
O artigo aborda a estratégia clássica de grade, descrevendo detalhadamente sua automação com um EA em MQL5 e analisando os resultados iniciais dos testes históricos. Também enfatiza a necessidade de manter posições por um longo período e considera a possibilidade de otimização de parâmetros-chave (como distância, take-profit e tamanhos de lotes) em futuras partes. O objetivo desta série de artigos é aumentar a eficiência da estratégia de negociação e sua adaptabilidade a diferentes condições de mercado.
Anotação de dados na análise de série temporal (Parte 6): Aplicação e teste de EA com ONNX Anotação de dados na análise de série temporal (Parte 6): Aplicação e teste de EA com ONNX
Nesta série de artigos, apresentamos vários métodos de anotação de séries temporais, que podem criar dados adequados à maioria dos modelos de inteligência artificial (IA). A anotação de dados direcionada pode tornar o modelo de IA treinado mais alinhado aos objetivos e tarefas do usuário, aumentar a precisão do modelo e até ajudar o modelo a alcançar um salto qualitativo!
Redes neurais de maneira fácil (Parte 72): previsão de trajetórias em condições de ruído Redes neurais de maneira fácil (Parte 72): previsão de trajetórias em condições de ruído
A qualidade da previsão de estados futuros desempenha um papel importante no método Goal-Conditioned Predictive Coding, com o qual nos familiarizamos no artigo anterior. Neste artigo, quero apresentar a vocês um algoritmo capaz de aumentar significativamente a qualidade da previsão em ambientes estocásticos, que incluem os mercados financeiros.
Funcionalidades do assistente MQL5 que você precisa conhecer (Parte 10): RBM não convencional Funcionalidades do assistente MQL5 que você precisa conhecer (Parte 10): RBM não convencional
As máquinas de Boltzmann restritas (Restrictive Boltzmann Machines, RBM) são, em um nível básico, uma rede neural de duas camadas capaz de realizar classificação não supervisionada através da redução de dimensionalidade. Vamos usar seus princípios básicos e ver o que acontece se a desenharmos e a treinarmos de forma não convencional. Será que conseguiremos obter um filtro de sinais útil?