Español
preview
Desenvolvendo um sistema de Replay (Parte 41): Iniciando a segunda fase (II)

Desenvolvendo um sistema de Replay (Parte 41): Iniciando a segunda fase (II)

MetaTrader 5Exemplos | 24 janeiro 2024, 09:57
248 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior,  Desenvolvendo um sistema de Replay (Parte 40): Iniciando a segunda fase (I), iniciamos a construção de um indicador para comportar o sistema de estudos e o mouse. Mas antes de começarmos de fato este artigo, gostaria de fazer uma pergunta: Você consegue, de alguma forma, imaginar, por que estamos criando este indicador ?!?! O que existe por detrás desta minha intenção de demonstrar como criar tal coisa ?!?! Ou o que será que estou tentando fazer, criando um indicador para o mouse ?!?!

Muito bem. Talvez tais questões não fazem muito sentido. Mas você já parou, em algum momento para observar os seus próprios código ?!?! Já prestou a devida atenção e notou o quanto repete, sempre as mesmas coisas em códigos diferentes ?!?! E com isto, muitos de seus códigos, passam a não tem a mínima consistência, ou estabilidade ?!?! Por favor, não me entenda mal. Longe de mim, dizer que você, que muitas das vezes pode ter anos de experiência em programação. Que de fato não está explorando adequadamente a linguagem MQL5, ou qualquer outra linguagem na qual você se especializou.

Só quero forçar você, de alguma forma, com anos de experiência em programação, a deixar de sempre fazer as mesmas coisas. Você precisa olhar com mais atenção o seu próprio trabalho. Parar de sempre ficar tendo que fazer as mesmas coisas por conta falta de pensar antes de programar as coisas.

Existe de fato, um motivo oculto por detrás de iniciarmos uma nova fase no sistema de replay / simulador. Este já foi falado, em um outro artigo. Estou cansado de fazer as coisas de uma forma duplicada em MQL5. Meus códigos privados, seguem uma determinada diretriz. No entanto, os códigos que vem para os artigos, estavam seguindo outra. Sempre tentando deixá-los mais simples possível e de fácil compreensão. Mas sempre vejo pessoas dizendo, que isto ou aquilo, seja sobre o MQL5, seja sobre o MetaTrader 5.

Quero mostrar que se pode fazer muito, mas muito mais do que grande parte tem conseguido fazer. Então prepare-se. Chega de ficar fazendo graça. A partir de agora, você irá ver como realmente são meus código. Não irei mostrar coisas particulares sobre determinados ativos, ou indicadores. Mas irei mostrar que o MQL5, assim como o MetaTrader 5, podem fazer muito mais do que você tem visto.

Então vamos começar fazendo o seguinte: No artigo anterior mencionei que iria ficar faltando uma pendencia a ser resolvida. Vamos resolver isto aqui e agora.


Modificando a classe C_Mouse

A tal pendencia é com relação a forma como podemos interagir com a o indicador de mouse. O código visto no artigo anterior está finalizado. O indicador não irá sofrer nenhuma mudança, pelo menos por um bom tempo. Mas existe uma questão que torna a coisa bastante complicada, quando vamos usar os dados do mesmo.

Para você compreender a fundo o que irei fazer. Será preciso você abandonar a ideia e conceitos de tudo que você já sabe sobre MetaTrader 5 e a linguagem MQL5. Esqueça tudo que você já viu sobre como fazer as coisas nela. Vamos pensar de uma forma totalmente diferente. Se você conseguir compreender e assimilar isto, conseguirá fazer um tipo de codificação onde a produtividade e estabilidade de seus código será cada vez maior.

Em artigos passados, falei sobre o fato de você olhar os processos que estão rodando no MetaTrader 5, não como sendo Indicadores, Expert Advisor, Scritps ou Serviços. Que você deveria abandonar esta ideia e ver tais processo como sendo funções. A ideia de funções talvez não seja a melhor definição para isto. Mas existe uma que se adequa mais perfeitamente: DLLs. Sim, pense em todos os processos que estão rodando no MetaTrader 5, principalmente os indicadores, como sendo DLLs.

Por que estou dizendo isto ?!?! Por que os mesmo conceitos envolvidos em DLLs, se aplicam ao que irei mostrar aqui como fazer. Neste momento o único indicador que temos é o do mouse. Desta forma você não terá a total e completa compreensão do que irá acontecer. Mas como toda grande construção, sempre se inicia no momento em que fincamos a pedra fundamental. Iremos fazer isto agora. Mas por conta de algumas questões, precisamos facilitar a forma de trabalhar com o conjunto a fim de não gerar um caos completo.

Se você observar no artigo: Desenvolvendo um sistema de Replay (Parte 31): Projeto EA - Classe C_Mouse (V), irá notar que a classe C_Mouse, assim como a classe C_Study, funcionam de uma determinada maneira. Mas a maneira como elas funcionam, NÃO nos permite fazer uso de uma programação simples a ponto de tirar proveito do indicador que utiliza tais classes. Atenção: Não estou falando que não conseguimos tirar proveito. Estou dizendo que para fazer isto precisaremos de uma programação extremamente complexa.

A ideia aqui então é simplificar esta programação. Para isto iremos remover algumas coisas da classe C_Study, passando elas para a classe C_Mouse. Mas também iremos promover uma adição, em termos de programação e estruturação, na classe C_Mouse, a fim de facilitar o uso do indicador como será visto nos próximos artigos.

O que iremos remover na classe C_Study, pode ser visto no fragmento logo abaixo:

01. #property copyright "Daniel Jose"
02. //+------------------------------------------------------------------+
03. #include "..\C_Mouse.mqh"
04. #include "..\..\Auxiliar\C_Mouse.mqh"
05. #include "..\..\Auxiliar\Interprocess.mqh"
06. //+------------------------------------------------------------------+
07. #define def_ExpansionPrefix "MouseExpansion_"
08. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
09. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2"
10. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3"
11. //+------------------------------------------------------------------+
12. #define def_AcessTerminal (*Terminal)
13. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal()
14. //+------------------------------------------------------------------+
15. class C_Study : public C_Mouse
16. {
17.     protected:
18.     private :
19. //+------------------------------------------------------------------+
20.             enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
21. //+------------------------------------------------------------------+
22.             struct st00
23.             {
24.                     eStatusMarket   Status;
25.                     MqlRates        Rate;
26.                     string          szInfo;
27.                     color           corP,
28.                                     corN;
29.                     int             HeightText;
30.             }m_Info;

Fragmento de código da classe C_Study

Observem o fragmento acima. Por conta que a localização do arquivo de cabeçalho C_Study.mqh foi modificado. A linha 04 foi substituída pela linha 03, e pelo motivo de não fazermos mais a ligação aqui com o arquivo de cabeçalho InterProcess.mqh, a linha 05 também foi removida.

Já a linha 20 foi removida e lançada para dentro da classe C_Mouse. O motivo desta transferência é facilitar a programação que será feita depois. Agora que já vimos as mudanças feitas na classe C_Study. Vamos passar para a classe C_Mouse, pois nela as mudanças foram bem mais profundas.

Você pode ver o novo código da classe C_Mouse, logo abaixo em destaque e na integra.

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

Código do arquivo C_Mouse.mqh

O motivo de colocar o código na integra, se deve ao fato de que não haverá, pelo menos por enquanto, nenhum arquivo no anexo. Então quem desejar utilizar as melhorias, poderá faze-lo, usando os códigos que estão sendo mostrados. Quando o sistema ficar em um estágio mais avançado, irei retornar os arquivos em anexo. Por hora eles ficarão indisponíveis. 

Se você observar irá ver que no código existem pontos destacados. Estes são pontos que apesar de terem alguma relevância, não irei entrar em detalhes sobre os mesmos. O motivo é que na maior parte das vezes os mesmos são autoexplicativos.

O código removido da linha 20 no fragmento da classe C_Study, veio parar na linha 19 da classe C_Mouse. Mas reparem na linha 18 do arquivo C_Mouse.mqh. Estou declarando uma clausula publica. Mas por que estou colocando esta clausula ?!?! O motivo é que sem ela, qualquer informação não irá ter o nível de acesso que precisamos. Precisamos de fato que tais dados sejam públicos. Normalmente usamos apenas uma vez a clausula, mas por conta de meu costume em lançar primeiro os dados protegidos, depois os privado e no último ponto os públicos, você irá ver esta mesma clausula na linha 122. Mas isto se deve a forma como estou acostumado a programar.

Na linha 20 temos também uma outra enumeração, que já existia na classe C_Mouse, mas que antes não era pública, assim como também a estrutura que se inicia na linha 21 e vai até a linha 32. Neste caso especifico da estrutura, notem que ela não esta sendo usada em nenhuma variável global publica. Não é de boa prática, declarar variáveis globais publicas em classes. Toda e qualquer variável global de classe deve sempre ser privativa ou protegida. Nunca pública.

Então a partir da linha 34 os dados não serão mais públicos. Neste ponto temos uma clausula protected, esta irá tornar dados, funções e procedimentos protegidos. Já explique sobre esta questão em artigos passados. Mas esta não é a questão. A questão é o teste presente na linha 39. Este teste não existia antes. Quando a variável que esta sendo testada, estiver com um valor verdadeiro, não será possível criar os objetos que estão sendo criados neste procedimento CreateObjectInfo. Mas por que estamos fazendo isto ?!?! Para compreender precisaremos caminhar um pouco mais nas linhas do código.

Caminhando um pouco mais, encontramos duas linhas, onde são declaradas duas variáveis, as linhas 68 e 70. Estas não existiam originalmente no código que estava sendo postado. No entanto aqui se faz necessário tais variáveis. Entre os motivos está justamente o que foi visto na linha 39. Permitir ou não a execução de um procedimento ou função. Este mesmo tipo de coisa irá acontecer nas linhas 165 e 194. No caso da 165 existe um motivo mais forte, que irei explicar logo mais. No entanto na linha 194 o motivo é o mesmo da linha 39. Evitar a execução da função ou procedimento, visto que elas não irão de fato trabalhar ou executar corretamente, quando estivermos no modo tradução.

Como você deve estar esperando e imaginando, tais variáveis são inicializadas no constructor da classe. Isto é fato. Mas, porém, toda via e entretanto, agora não mais iremos contar um um constructor de classe. Agora temos dois constructores para a classe C_Mouse 😱😵😲. Se você esta iniciando na programação orientada a objetos deve está apavorado com isto. Mas isto é algo perfeitamente normal em OOP ( programação orientada em objetos ). Um dos constructores está sendo codificado entre as linhas 124 a 129. Já o outro constructor está codificado entre as linhas 131 a 147. Mas apesar de termos dois constructores, contamos apenas com um destructor.

Devido ao fato de que um dos constructores atenderá um determinado tipo de demanda e o outro constructor irá atender a outro tipo de demanda. Precisamos de alguma forma separar eles. Isto é feito usando variáveis globais privativas. Sem fazer isto, seria muito pouco provável que iriamos conseguir fazer algum tipo de separação.

Então agora vamos entender o que, e por que temos dois constructores. O primeiro motivo, é que não faz sentido, criar um segunda classe, apenas para cobrir a demanda que precisamos. O segundo motivo, é que podemos direcionar o fluxo de execução, de maneira a reutilizar o código já existente. Isto sem precisar de fato criar herança, ou manipular dados, funções e procedimentos.

Se você reparar, irá notar que a única coisa que fizemos até o momento, foi isolar 2 códigos. Sendo um público e o outro protegido. De maneira que se estivermos usando a classe C_Mouse, a fim de atender uma determinada demanda, teremos um modelo de uso. Mas quando formos atender outro tipo de demanda, teremos um modelo diferente. Mas a forma de programar usando a classe será sempre a mesma. Isto quando formos usar a mesma em nossas rotinas.

Observando o constructor que esta codificado entre as linhas 131 e 147. Você irá notar que ele é praticamente o mesmo visto em Desenvolvendo um sistema de Replay (Parte 31): Projeto Expert Advisor - Classe C_Mouse (V). Isto é proposital, justamente pelo motivo de que este constructor será responsável por atender a demanda da classe, quando o código de fato está fazendo uso dos objetos presentes na classe. A diferença deste para aquele constructor original, é a linha 133, que inicializa a nova variável, de forma a dizer que estaremos usando a classe de forma original.

O fato de isto está acontecendo, faz com que grande parte do código se mantenha, e toda e qualquer explicação dada naquele artigo especifico irá ser válida. Então podemos focar na segunda forma de usar a classe. Para isto vamos primeiro ver o código do constructor que se encontra entre as linhas 124 e 129. Apesar da simplicidade envolvida, você deve notar o fato de estarmos inicializando todos as variáveis realmente necessárias.

Isto promove uma maior simplicidade no momento que formos usar a classe como um tradutor. Talvez você não tenha entendido como e por que de fazer isto. Mas sim iremos usar a classe C_Mouse como um tradutor do indicador de mouse. Isto parece ser algo bastante complexo e difícil de ser feito. Não é mesmo ?!?! Mas não, isto esta longe de ser complexo de ser feito. Mas existe um motivo de fazermos assim.

Para entender, é preciso pensar um pouco: Quando você vai criando programas, sejam eles indicadores, EAs, scripts ou qualquer outra coisa. Muitas vezes você vai adicionando coisas que se repetem bastante. Uma destas coisas é justamente as rotinas de tratamento do mouse. Se em cada EA que você for criar, você tiver que adicionar rotinas de maneira a gerar estudos, ou analisar o mouse. Seu código nunca irá de fato ter uma boa robustez. No entanto, quando criamos algo que irá se manter no tempo, podendo ser melhorado, independentemente de outros código. Ai teremos uma nova forma de programa. Naturalmente o código irá se tornar muito mais robusto e eficaz.

Aqui na classe, iremos sempre presumir que o indicador de mouse estará presente no gráfico. Lembre-se: Nunca devemos presumir algo, mas aqui iremos presumir que o indicador estará no gráfico. Quem for fazer uso da classe C_Mouse como tradutor, deverá obrigatoriamente saber deste fato. A classe estará presumindo que o indicador se encontra no gráfico.

Uma vez que você tenha entendido este fato, podemos passar para uma outra questão. Não precisamos de fato que a classe C_Mouse faça a tradução do indicador para nos. Podemos fazer isto diretamente no programa que estivermos criando. Porém, é muito mais cômodo e simples permitir que a classe faça esta tradução para nos. O motivo é que se por um acaso, não desejarmos de fato fazer uso do tradutor. Tudo que iremos precisar fazer, será modificar o constructor da classe e adicionar a chamada de tratamento de eventos no nosso programa.

Notarão como será bem mais simples fazer as coisas. Mas ainda assim quero e desejo que você passe a fazer uso do sistema de tradução. Então para compreender como o sistema de tradução funciona é preciso observar apenas e somente uma única rotina. A GetInfoMouse. Esta se encontra na linha 163. Originalmente esta era uma rotina const. Mas agora precisamos que ela não seja mais assim. Apesar de você não poder mudar os dados fora da rotina, precisamos que eles sejam modificados dentro da rotina.

Se você observar o artigo anterior, Desenvolvendo um sistema de Replay (Parte 40): Iniciando a segunda fase (I). Irá notar, que é muito complicado interpretar, ou melhor dizendo, manter um padrão na codificação a fim de interpretar os dados do buffer do indicador. Justamente por conta disto, estou mostrando como fazer para modificar a classe C_Mouse, a fim de criar tal padronização. Para você ter uma noção de como seria complicado, basta pensar no fato de que, toda a vez, que fosse necessário usar o indicador de mouse, você teria que codificar a mesma coisa que é vista nas rotinas CheckClickGetInfoMouse. Resumindo: Uma santa trabalheira.

Mas vamos ver o que está acontecendo aqui, começando com a rotina CheckClick. Nesta rotina simplesmente carregamos e testamos, o valor que está nos dados do mouse. Seja como indicador, seja como tradutor. De qualquer forma, seja para analise do indicador, seja no uso do próprio indicador. Na linha 160, iremos verificar se o botão a ser analisado foi ou não ativado, ou seja se houve ou não um clique.

Aqui mora um questão, da qual você precisa compreender. Independente de estarmos usado a classe C_Mouse como interpretador ou como indicador. Sempre teremos algum tipo de resposta vinda da função CheckClick e GetInfoMouse. Sempre. E esta resposta, irá sempre refletir o que está sendo feito como mouse. Independente de onde estaremos usando a informação.

Então para compreender a resposta dada por CheckClick, temos que observar e compreender o funcionamento da função GetInfoMouse. Esta função é vista a partir da linha 163.

Na linha 165 da função, efetuamos um teste. Este tem como proposito, verificar se estaremos usando a classe como interprete ou como indicador. Caso o teste passe, a classe estará no modo interprete, e neste caso precisaremos acessar o buffer do indicador. Bem, aqui mora uma questão, que é ao mesmo tempo complicada e simples de ser trabalhada. Vamos primeiramente ver a parte simples.

Na linha 171 zeramos os dados da estrutura de retorno. Esta estrutura, na verdade é um variável global privativa da classe, que esta sendo declarada na linha 58. Tendo isto ocorrido, fazemos o pedido para que o MetaTrader 5, leia o buffer do indicador. Se tivermos sucesso na leitura, que é feita na linha 172, iniciaremos a tradução dos dados para a estrutura de retorno.

A forma de se traduzir os dados, seguem exatamente a mesma lógica usada para codificá-los. Esta codificação pode ser vista ao olhar a rotina SetBuffer presente no artigo anterior. Para facilitar as coisas você pode ver aquela mesma rotina no fragmento logo abaixo:

102. inline void SetBuffer(void)
103. {
104.    uCast_Double Info;
105.    
106.    m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
107.    m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
108.    Info._datetime = (*Study).GetInfoMouse().Position.dt;
109.    m_Buff[m_posBuff + 1] = Info.dValue;
110.    Info._int[0] = (*Study).GetInfoMouse().Position.X;
111.    Info._int[1] = (*Study).GetInfoMouse().Position.Y;
112.    m_Buff[m_posBuff + 2] = Info.dValue;
113.    Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
114.    m_Buff[m_posBuff + 3] = Info.dValue;
115. }

Fragmento do código do indicador

Observem que a informação colocada na linha 107 no fragmento do indicador, é decodificada na linha 174 da classe. Em grande parte do tempo a tradução é simples. Mas você deve se manter dentro das regras feitas durante a codificação. Isto pode ser melhor entendido no momento em que armazenamos os dados na linha 112 do fragmento do indicador. Notem que neste caso estamos trabalhando com 2 valores compactados em um valor double. No momento de traduzir isto, temos que fazer uma operação contrária. Esta é feita na linha 177 da classe, onde capturamos o valor e nas linhas 178 e 179, colocamos os valores no local correto, para uso posterior.

A mesma coisa se dá na linha 113 do fragmento do indicador. Onde armazenamos o valor do clique do mouse. E o traduzimos na linha 181 na classe. Mas vamos olhar novamente, a linha 113, no fragmento do indicador. Observem que o operador ternário irá armazenar um valor zero, caso estejamos no modo estudo. É importante você entender isto. Pois caso estejamos fazendo um estudo via indicador, e a classe esteja sendo usada para traduzir isto. Quando você for testar se aconteceu um clique usando a função CheckClick, esta irá retornar false, isto caso estejamos executando um estudo. Este tipo de coisa irá acontecer sempre. Desde é claro estejamos usando o indicador e a classe como tradutora.

Esta foi a parte simples e fácil de entender. Mas como foi dito antes, existe uma outra parte. A parte difícil e complicada.

Esta parte complicada acontece quando estamos usando a classe como interprete. No entanto não temos acesso ao buffer do indicador. Isto normalmente se dará quando o indicador vier a ser removido do gráfico. Quando isto ocorrer a linha 169 irá gerar um handle nulo, fazendo com que não tenhamos nenhum buffer a ser lido. Mas mesmo assim teremos a execução da linha 171 que irá zerar os dados para nos.

Isto pode causar diversos distúrbios e falhas ao interpretar ou tentar fazer algo com os dados do indicador. Mas pelo fato de que ele sempre irá informar zero, não teremos de fato nenhum teste positivo de clique ou movimentação. Mas ainda continuamos tendo problemas com relação a isto. Não neste caso especifico. Existem outros casos que isto será um problema para nos. Quando isto ocorrer irei retornar a este assunto.


Usando a classe C_Mouse como tradutora

Para fins de demonstração, neste primeiro momento, já que depois iremos expandir isto.

Vamos ver o código de um Expert Advisor, este irá ter o mesmo funcionamento que é visto no artigo  Desenvolvendo um sistema de Replay (Parte 31): Projeto Expert Advisor - Classe C_Mouse (V), no entanto iremos fazer com que ele seja codificado de maneira diferente.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Generic EA for use on Demo account, replay system/simulator and Real account."
04. #property description "This system has means of sending orders using the mouse and keyboard combination."
05. #property description "For more information see the article about the system."
06. #property version   "1.41"
07. #property icon "/Images/Market Replay/Icons/Replay - EA.ico"
08. #property link "https://www.mql5.com/en/articles/11607"
09. //+------------------------------------------------------------------+
10. #include <Market Replay\Auxiliar\C_Mouse.mqh>
11. //+------------------------------------------------------------------+
12. C_Mouse *mouse = NULL;
13. //+------------------------------------------------------------------+
14. int OnInit()
15. {
16.     mouse = new C_Mouse("Indicator Mouse Study");
17.     
18.     return INIT_SUCCEEDED;
19. }
20. //+------------------------------------------------------------------+
21. void OnTick() 
22. { }
23. //+------------------------------------------------------------------+
24. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
25. {
26.     C_Mouse::st_Mouse Infos;
27.     
28.     switch (id)
29.     {
30.             case CHARTEVENT_MOUSE_MOVE:
31.                     Infos = (*mouse).GetInfoMouse();
32.                     Comment((string)Infos.Position.Price + " :: [" + TimeToString(Infos.Position.dt), "]");
33.                     break;
34.     }
35. }
36. //+------------------------------------------------------------------+
37. void OnDeinit(const int reason)
38. {
39.     delete mouse;
40. }
41. //+------------------------------------------------------------------+

Código do Expert Advisor

Apesar de não parecer. O código visto acima, tem o mesmo comportamento, do visto no artigo a pouco mencionado. Com uma vantagem. O código acima, é consideravelmente mais simples, prático e robusto, do que o visto no artigo mencionado. O motivo disto, não é simples de ser compreendido neste exato momento. Mas aos poucos você irá compreender isto, conforme formos avançando na codificação.

Você pode notar que não estamos de fato fazendo qualquer tipo de coisa extremamente complicada. Estamos simplesmente supondo que o indicado estará no gráfico. No linha 16 dizemos qual é o nome curto do indicador. Já na linha 39 removemos a classe de C_Mouse.

Agora quero que reparem nas linhas 31 e 32. Elas irão lançar os dados no mouse no gráfico, a fim que de você possa observar eles.

O grande barato nisto é o fato de que se você na linha 16 declarar que estará usando a classe como indicador, bastará adicionar uma chamada a função DispatchMessage, isto na rotina OnChatEvent, para que tenha o mesmo efeito do uso do indicador gráfico. Ou seja a programação não irá mudar. Apenas se adaptar ao que queremos e estaremos usando.

Caso você deseje usar, e aconselho a fazer isto, a rotina como tradutora. O Expert Advisor irá fazer a analise a ponto de saber onde e o que o mouse está fazendo. Sempre reportando as coisas de maneira adequada.


Conclusão

É importante que você entenda, conheça e compreenda, o como as coisas estão acontecendo. Caso contrário você irá ficar completamente perdido nos próximo artigos. Onde já não iremos usar as coisas com antes. Iremos usá-las de uma maneira muito mais sofisticada. Apesar do que foi mostrado neste dois últimos artigos parecer ser algo muito complexo e extremamente avançado, isto para quem de fato não tem tanto conhecimento em programação. Tudo que foi visto nestes dois últimos artigos é apenas uma pequena preparação para podermos adicionar a aplicação de Chart Trader. Ou seja, nos próximos artigos começaremos a adicionar e a desenvolver o Chart Trader, para nosso sistema. Esta aplicação serve justamente para que possamos fazer operações diretamente a mercado. Algo bastante útil e necessário, principalmente pelo fato de que não podemos contar com o sistema de ordens a mercado, presente no MetaTrader 5. Precisamos assim criar o nosso. E este é o Chart Trader.

Mas independentemente disto, aqui ainda estamos trabalhando em um nível básico do MQL5. Mesmo que você imagine e sinta que este material é algo muito avançado, ele ainda está no seu nível mais básico. Talvez não tanto quanto poderia ser. Mas não se sinta mal por imaginar e ver as coisas de maneira diferente. Sentindo que eu possa estar de zoeira, ao dizer que tudo isto é simples e fácil. Na verdade entendo se você achar que isto de fato pode ser muito avançado. Mas isto se deve ao fato de que você não estava explorando o MQL5, de maneira realmente profunda. Sempre que você caminha na superfície achará que se encontra em uma alta colina, até que alguém vem e lhe mostre que você esteve durante todo este tempo apenas caminhando ao nível do mar. Então acreditem, em breve, você irá considerar todo este material visto, nestes últimos artigos, uma brincadeira de criança. Algo que qualquer júnior conseguiria fazer. Então se prepare. Pois esta vindo chumbo grosso pela frente. E quando ele chegar, iremos voltar nestas questões deste indicador de mouse.


Arquivos anexados |
Anexo.zip (420.65 KB)
Redes neurais de maneira fácil (Parte 53): decomposição de recompensa Redes neurais de maneira fácil (Parte 53): decomposição de recompensa
Já falamos várias vezes sobre a importância de escolher corretamente a função de recompensa que usamos para incentivar o comportamento desejável do Agente, adicionando recompensas ou penalidades por ações específicas. Mas a questão de como o Agente interpreta nossos sinais permanece em aberto. Neste artigo, discutiremos a decomposição da recompensa em termos de transmissão de sinais individuais ao Agente a ser treinado.
Melhore os gráficos de negociação com uma interface gráfica interativa baseada em MQL5 (Parte III): Interface de negociação simples e móvel Melhore os gráficos de negociação com uma interface gráfica interativa baseada em MQL5 (Parte III): Interface de negociação simples e móvel
Nesta série de artigos, exploramos a integração de interfaces gráficas interativas em painéis de negociação móveis no MQL5. Na terceira parte, usamos os desenvolvimentos das partes anteriores para transformar painéis de negociação estáticos em dinâmicos.
Criando um Expert Advisor simples multimoeda usando MQL5 (Parte 1): Sinais baseados no ADX em combinação com o Parabolic SAR Criando um Expert Advisor simples multimoeda usando MQL5 (Parte 1): Sinais baseados no ADX em combinação com o Parabolic SAR
Neste artigo, por EA multimoeda, entendemos um Expert Advisor ou robô de negociação capaz de negociar (abrir/fechar ordens, gerenciar ordens, etc.) mais de um par de símbolos a partir de um único gráfico.
Redes neurais de maneira fácil (Parte 52): exploração com otimização e correção de distribuição Redes neurais de maneira fácil (Parte 52): exploração com otimização e correção de distribuição
À medida que a política do Ator se afasta cada vez mais dos exemplos armazenados no buffer de reprodução de experiências, a eficácia do treinamento do modelo, baseado nesse buffer, diminui. Neste artigo, examinamos um algoritmo que aumenta a eficácia do uso de amostras em algoritmos de aprendizado por reforço.