Español
preview
Desenvolvendo um sistema de Replay (Parte 56): Adequando os Módulos

Desenvolvendo um sistema de Replay (Parte 56): Adequando os Módulos

MetaTrader 5Exemplos | 11 julho 2024, 15:44
215 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Desenvolvendo um sistema de Replay (Parte 55): Módulo de controle,  fizemos algumas modificações que permitiram que o indicador de controle fosse criado de modo que não viesse a ser necessário fazer uso mais das variáveis globais de terminal. Pelo menos no que diz respeito a manter as informações, e configurações que fossem ajustadas pelo usuário.

Apesar de aquilo funcionar maravilhosamente bem, e ser bastante estável quando o sistema fosse colocado em um gráfico que contivesse um dado número de barras, o sistema a todo momento era finalizado por falhas de range, quando era tentado usar dados referentes a um ativo customizado.

O motivo, não é por que o ativo customizado, esteja de fato sujeito a falhas e a gerar problemas. Mas sim por conta da própria base de dados que muitas das vezes não é suficiente para promover o correto funcionamento de todo o sistema. Basicamente o fator problemático é o buffer.

Você pode estar pensando: Mas como o buffer é o problema ?!?! O sistema funciona quando usamos em um ativo real, mas quando usamos um ativo customizado o mesmo falha, e o motivo de tal falha é o buffer do indicador ?!?!

Sim, o motivo é o buffer. Mas não pelo motivo que você talvez esteja imaginando. E sim pelo motivo de que quando criamos um ativo customizado, talvez o buffer não tenha um tamanho mínimo necessário para comportar os dados que precisamos colocar nele.

Ao ver tal fato, você pode pensar que bastaria alocarmos memória e o problema iria se resolver. Bem, em se tratando de MQL5, a coisa não é assim tão simples. A alocação de memória a fim de colocar dados em um buffer de indicador, não se dá como você muito provavelmente estará imaginando. A alocação de memória, se dá de fato, se baseando na quantidade de barras presentes no gráfico. Então não adianta você usar alguma função, a fim de alocar memória, pois ela de fato não será usada como você pode possivelmente está imaginando, ou desejando fazer.


Entendendo o problema

O real problema na verdade não se dá no indicador de controle, mas sim no indicador de mouse. Mas ao efetuar a correção a fim de solucionar a falha, iremos criar algo que fará com que o indicador de controle também mude, mas as mudanças serão vistas mais a frente, neste mesmo artigo. Mas antes vamos entender a falha e como ela de fato acontece.

Se você fizer uso, do indicador de mouse, presente nos artigos anteriores. Ao colocar ele em um gráfico, de um ativo customizado, que tem por exemplo 60 barras de um minuto, não irá ter problemas nos tempos gráficos iguais ou inferiores a 10 minutos. Mas assim que tentar usar um tempo gráfico superior a 10 minutos, terá a informação do MetaTrader 5, que o indicador de mouse, acabará de gerar um erro de range.

Mas por que isto aconteceu ?!?! O motivo é que o indicador de mouse presente até o artigo anterior, precisa de 6 posições para armazenar os dados no seu buffer de indicador. Então a matemática responde o que de fato aconteceu. Com 60 barras de um minuto, você poderá trocar o tempo gráfico até o valor de 10 minutos, o que irá fazer com que tenhamos 6 barras no gráfico. Estas 6 barras irão fazer com que tenhamos as seis posições necessárias no buffer, a fim de poder colocar os dados nele. Mas assim que colocarmos um tempo gráfico maior, o número de barras, presentes no gráfico, será menor que seis.

E é neste ponto que o indicador de mouse irá gerar um erro de range. Pois ele irá tentar escrever em uma posição de memória que de fato não foi alocada pelo MetaTrader 5, a fim de que o buffer possa ser escrito.

Esta é a tal falha, e existem duas formas de corrigirmos ela. A primeira é colocar uma quantidade suficiente de barras no ativo customizado, a ponto de que ele em qualquer tempo gráfico, possa ter no mínimo seis barras presentes no gráfico. Está de certa forma, não é uma solução assim tão adequada. Visto que podemos acessar um tempo gráfico mensal, ou seja teríamos que ter no mínimo seis meses de barras de um minuto carregadas no gráfico de um ativo customizado, a fim de não precisarmos modificar o indicador de mouse. Isto apenas para evitar que o mesmo venha a gerar o erro de range.

Pessoalmente, e acredito que muitos irão concordar com a minha visão, isto está longe de ser uma solução adequada, ainda mais se tratando de um sistema de replay / simulador. Se o sistema fosse de fato voltado apenas e somente para replay, talvez esta solução pudesse ser viável, isto desde é claro, você fizesse os estudos se baseando em apenas e somente um tempo gráfico, ou tempos gráficos inferiores. Mas já que podemos usar o sistema como uma forma de simular movimentos de mercado. Tal solução é totalmente inviável, precisando assim de algo um pouco mais elegante.

E é justamente isto que iremos fazer. Iremos modificar o indicador de mouse, de forma que a informação seja de alguma maneira compactada, em apenas uma única posição dentro do buffer. Assim iremos precisar de apenas e somente uma única barra presente no gráfico, a fim de que o indicador possa fazer o seu trabalho.


Iniciando a implementação da solução

O fato de decidir por usar a solução de compactar as informações em uma única posição, se deve ao fato, de que para o sistema de replay / simulação, será consideravelmente mais simples, adicionarmos e mantermos no mínimo uma barra no gráfico, do que fazer qualquer outro tipo de coisa.

Mas o motivo principal, é que podemos desejar criar uma simulação de mercado, e ao fazer isto, não precisaremos usar uma quantidade gigantesca de barras, podemos apenas usar a quantidade que de fato precisamos e desejamos, e o próprio serviço de replay / simulador irá fazer com que o sistema tenha o mínimo de estabilidade necessária. Isto a ponto de podermos fazer uso dos mesmos meios, que poderemos fazer uso em uma conta real ou uma conta de demonstração.

Resumindo: Iremos tornar o buffer do indicador de mouse, algo bem menor. Onde apenas uma única posição será usada, mas mesmo assim ainda iremos ter a mesma quantidade de informações nos sendo retornadas quando viermos a fazer um requerimento ao indicador a fim de ler o buffer. Um detalhe que vale ser mencionado a este respeito aqui e agora. Se você fizer a leitura diretamente do buffer do indicador de mouse, irá obter apenas dados em uma posição do mesmo. Tais dados deverão ser traduzidos a fim de que possam ser realmente úteis, mas fazendo uso da classe C_Mouse, poderemos fazer uso de uma função que estará disponível a fim de que esta tradução seja feita de forma adequada.

Dito isto, podemos passar a fase de implementação real. E para de fato começar a fazer isto, a primeira das coisas a serem modificadas é o arquivo de definição. Este tem o seu código exposto logo abaixo.

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.    uint     _32b[sizeof(double) / sizeof(uint)];    // 2 Informations
22.    ushort   _16b[sizeof(double) / sizeof(ushort)];  // 4 Informations
23.    uchar    _8b [sizeof(double) / sizeof(uchar)];   // 8 Informations
24. };
25. //+------------------------------------------------------------------+
26. enum EnumEvents    {
27.          evHideMouse,               //Hide mouse price line
28.          evShowMouse,               //Show mouse price line
29.          evHideBarTime,             //Hide bar time
30.          evShowBarTime,             //Show bar time
31.          evHideDailyVar,            //Hide daily variation
32.          evShowDailyVar,            //Show daily variation
33.          evHidePriceVar,            //Hide instantaneous variation
34.          evShowPriceVar,            //Show instantaneous variation
35.          evSetServerTime,           //Replay/simulation system timer
36.          evCtrlReplayInit           //Initialize replay control
37.                   };
38. //+------------------------------------------------------------------+

Código fonte do arquivo Defines.mqh

Praticamente não existem grandes diferenças aqui, isto em termos gerais. Mas se você observar bem, irá perceber que entre as linhas 21 e 23 houve mudanças. Tais mudanças visam justamente possibilitar um melhor uso dos bits. Então para facilitar a identificação do tipo de informação que estaremos tratando, usei uma notação simples, onde _32b significa 32 bits, _16b significa 16 bits e _8b significa 8 bits. Assim quando for feita algum tipo de acesso, poderemos saber exatamente qual o número de bits que estaremos de fato usando. Lembrando que um valor double representa 64 bits, então cada um dos pacotes terá um comprimento compatível a fim de caber nestes 64 bits.

Desta forma _32b irá conseguir conter 2 valores, _16b poderá ter 4 valores e _8b poderá ter 8 valores. Mas prestem atenção ao fato de que estamos usando uma união, então podemos colocar combinações entre estes conjuntos. No entanto, para fazer isto de maneira correta você deve entender que todo array em MQL5 se baseia no sistema de numeração usado em C/C++, ou seja, iniciamos sempre em zero, e cada nova posição será sempre incrementada em uma unidade.

Neste ponto, muitos podem começar a ficar confusos se não tiverem conhecimento, ou uma base de como as coisas funcionam em C/C++, pois o termo unidade aqui, talvez não descreva de forma tão expressiva o que de fato teremos que usar ao incrementar o valor de index a fim de conseguir colocar os dados no local correto dentro do pacote. E o mal, ou não entendimento deste conceito, irá fazer você ficar completamente perdido, ou no mínimo não conseguir compreender como estarei de fato compactando as informações.

Antes de realmente vermos as mudanças feitas no indicador de mouse, vamos dar uma rápida passada pelo indicador de controle. E o motivo disto é pelo simples fato de que no indicador de controle, tudo que foi preciso fazer, foi adequar as funções, procedimentos e variáveis os novos tipos que estão presentes no arquivo de cabeçalho Defines.mqh. Vamos então começar vendo o código da classe, este pode ser visto logo a seguir.

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

Código fonte do arquivo C_Control.mqh

Observando o código, você irá notar que não existem diferenças, pelo menos não visíveis entre este código visto acima e o mesmo código presente no último artigo em que este arquivo de cabeçalho apareceu. Mas por conta de aconteceu algumas mudanças no arquivo de definição, algumas partes do código sofreram algumas pequenas mudanças. Onde você pode notar que estamos fazendo uso de uma conversão explicita de tipos. Um destes pontos que você pode notar isto se encontra na linha 164 e 165, onde informamos ao compilador que desejamos que um dado tipo seja explicitamente utilizado.

Observe que na linha 165, temos também uma mudança. Antes usávamos constantes inteiras, agora usamos constantes curtas. Mas apesar de estamos usando constantes sinalizadas, ou seja, que podem ser representadas negativamente, o valor a ser colocado no array será do tipo não sinalizado. Neste momento você pode pensar que isto irá provocar uma falha no entendimento dos valores. Mas se você pensou isto é por que não entende de fato como os valores são representados em termos binários. Neste caso sugiro você estudar representação binária de valores, assim você irá conseguir entender por que podemos colocar valores negativos em um sistema que não iria os representar e ainda assim transferir as informações sem perda de qualidade.

Neste mesmo código, você pode ver na linha 180 uma outra conversão explicita de tipos. Onde agora um valor que está em uma variável não sinalizada passa a ser colocada em uma variável sinalizada. Esta conversão de tipos, permite que valores negativos voltem a ser representados de maneira adequada. Observem que em todo o código de tratamento do evento customizado, de inicialização do indicador de controle, temos este tipo de modelagem, então estude com calma este fragmento presente entre as linhas 178 e 186, pois aqui fazemos o uso intensivo deste tipo de conversão.

É bem verdade, que a compactação feita aqui, neste indicador de controle, não foi feito de maneira assim tão profunda, a fim de usar mais adequadamente os bits presentes em uma double, mas isto se deve ao fato de que a informação aqui não necessita assim de um grau tão avançado. Agora uma última coisa sobre o código do indicador de controle. Já que aconteceu apenas mudanças referentes ao número da versão e ao link no código do indicador mesmo, não irei repetir aqui novamente o mesmo código. Então tudo que você precisará de fato fazer será modificar o arquivo de cabeçalho visto no artigo anterior por este presente neste artigo. O código do indicador irá permanecer igual, não tendo nenhuma outra diferença além da que foram mencionadas. Podendo assim ser usado sem problemas.

Agora com relação ao indicador de mouse, a coisa se torna um pouco mais complicada. Por conta disto precisaremos de fato de todos os 3 arquivos que serão utilizados para criar o indicador. Estes serão vistos e explicados no tópico abaixo.


Implementando a solução no indicador de mouse

Como você pode ter visto no tópico acima, foi preciso fazer algumas pequenas modificações no código do indicador de controle, mas tais modificações apenas foram necessárias no arquivo de cabeçalho. Mas aqui no indicador de mouse a coisa é bem diferente, e consideravelmente muito mais complicada.

Então vamos as mudanças. Primeiramente vamos ver como ficou a nova classe C_Mouse, esta pode ser vista logo a seguir.

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

Código fonte do arquivo C_Mouse.mqh

Talvez você não perceba logo de cara as mudanças que aconteceram. Isto por que grande parte delas será apenas na questão de tipagem das variáveis. Mas mesmo assim merecem ser mencionadas e o correto entendimento irá lhe ajudar a ver quais são as limitações que este indicador de mouse contém. E sim ele tem limitações, mas você logo entenderá quais são e assim poderá usá-lo de uma forma adequada, de modo que tais limitações não venham a afetar o seu uso.

Em primeiro lugar, e logo de cara você pode ver entre as linhas 21 e 24, que estamos usando um tipo SHORT, antes era o tipo INT, assim como na linha 28 o tipo da variável foi modificada.

Antes que você entre em desespero ao imaginar que estamos gerando um caos total ao fazer tais mudanças nos tipos, devo lembrar a você que algumas características e peculiaridades entre os tipos SHORT e o INT.

O tipo INT, é no MQL5, um tipo de 32 bits, ou seja, o valor pode ir de -2 147 483 648 a 2 147 483 647, isto considerando-o como um valor sinalizado. Já se ele não for sinalizado, ou seja, irá ser apenas positivo, o valor pode ir de 0 a 4 294 967 295. Já o tipo SHORT é um valor do tipo 16 bits, ou seja, ele pode ir de -32 768 a 32 767, caso seja sinalizado, ou no caso de não sinalizado, ir de 0 a 65 535.

Agora quero que prestem atenção a um fato, e me respondam: Qual é a dimensão em termos de pixel de seu monitor ?!?! E por que estou perguntando isto ?!?! O motivo é que fazer uso de um INT para representar a dimensão cartesiana ( X e Y ) em um monitor é pura ignorância. Não me entenda mal, mas você literalmente está desperdiçando muita informação. Para você ter uma noção, um monitor de 8K de resolução, o que é um baita de um monitor, tem 7 680 pixels horizontais por 4 320 pixels verticais. Ou seja, um monitor com uma resolução incrivelmente alta tem menos de 2 elevado a 13 posições tanto na horizontal, quanto na vertical. Valor este que pode ser perfeitamente comportado em uma variável do tipo SHORT, mesmo uma sinalizada onde temos 2 elevados a 16 posições possíveis. Assim sendo, ainda nos sobra 3 bits livres para fazer qualquer coisa.

Mas como não precisamos de tantos bits assim, uma simples otimização, como a que está sendo feita, já nos será mais que o suficiente. Lembrando mais uma vez que se fossemos usar INT poderíamos usar apenas 2, mas usando SHORT poderemos usar 4. Isto dentro de um mesmo comprimento double que é de 64 bits. Como precisamos apenas de 2 SHORT para representar as posições ( X e Y ) que irão indicar a posição do mouse no monitor, então nos sobra outras 2 SHORT para fazermos outras coisas. Lembrando novamente que temos 3 bits sobrando em cada uma das SHORT usadas na representação das coordenadas do mouse no monitor, e isto para uma tela de 8K.

Como, como você pode ver podemos a questão da posição, não é a nossa limitação. Nossa limitação é outra. Se você vem acompanhando esta sequência de artigos deve ter notado que em diversos momentos falei que iriamos permitir que o indicador de mouse nos fornecesse algumas informações para serem usadas em estudos de forma mais simples e rápida. E é aqui que mora a limitação. valores de preço e de tempo não poderão ser colocados diretamente no buffer do indicador de mouse. Isto por que, tais valores iriam exigir 64 bits para cada um deles, e não podemos nos dar ao luxo de adicionar duas posições pra isto. Por conta deste fato, precisamos fazer algo um pouco diferente. Assim nasce algumas mudanças no código da classe C_Mouse.

A primeira mudança que você irá notar se encontra na linha 117. Prestem atenção ao seguinte fato: Para não precisar modificar fortemente todos os tipos presentes no indicador de mouse, decidi manter as posições em INT. Pelo menos dentro da classe. Mas uma vez fora da classe as coisas irão se ajustar ao que precisamos de fato.

Então este procedimento na linha 117, irá traduzir as coordenadas de tela, que são informadas pelo sistema operacional, em coordenadas ajustadas pelo MQL5, a ponto de ficarem compatíveis com o que é visto no gráfico. Preste bastante atenção a isto. Este procedimento é privativo da classe, ou seja, nenhum código fora da classe terá acesso a ele. No entanto ele faz a tradução dos dados, fazendo com que o funcionamento continue idêntico ao que era feito antes. Ou seja, as mudanças ficaram transparentes ao sistema. Se você já estava usando este indicador e fazendo uso da classe a fim de traduzir as coisas, não terá problemas com relação aos dados.

Para entender isto, você deve olhar a linha 170. Esta função é que irá traduzir os dados do indicador. Repare que nada mudou, apesar de que o código interno desta função sofreu modificações, pois antes esperávamos seis valores sendo retornados, mas agora esperamos apenas um único valor. Mas prestem bastante atenção ao como estamos trabalhando agora. Repare que na linha 179 buscamos o valor no buffer do indicador, caso algum valor seja retornado, na linha 181, iremos atribuir o valor para o sistema de tradução. Observem que na linha 182, fazemos a captura e tradução do valor a fim de conseguir saber o status dos botões, repare bem no index que está sendo utilizado e do comprimento em termos de bits que estamos usando. Já na linha 183, enviamos os dados para o procedimento que irá converter as coordenadas de tela, ou seja, o X e Y em outros tipos que esperamos de fato usar, isto dentro do MetaTrader 5, ou via alguma outra aplicação que vier a usar o indicador de mouse como auxiliar. Virão que apesar das mudanças, as mesmas ficaram contidas apenas a função, não afetando em nada qualquer coisa fora da classe.

Agora vamos a uma questão um pouco mais complicada, porém primordial para que tudo funcione de fato. Esta está na linha 191, onde declaramos um procedimento que irá colocar os dados no buffer do indicador. Antes este mesmo procedimento fazia parte do código presente no indicador. Porém por motivos práticos decidi colocar ele aqui, no código da classe, e o principal motivador para isto é limitar o acesso aos dados da classe.

Então vamos ver como as coisas ficaram. Na  linha 193, declaramos a nossa variável de compactação. Na linha 195 iniciamos a compactação dos dados. Agora preste muita atenção ao que irá de fato acontecer, isto para que você compreenda os index que estão sendo usados no acesso dos arrays.

O array _8b, contem 8 posições, já o array _16b contém 4 posições. Ambos se iniciam com o index zero. Mas preste atenção a uma coisa o index zero do array _16b, faz parte dos index zero e um do array _8b. Ou seja, quando na linha 195, usamos o index zero no array _8b, estamos ocupando o index zero do array _16b, mas como a informação que precisamos colocar irá precisar apenas do index zero, o index um do array _8b irá ficar vago. Mas mesmo que o index um esteja vago, você não poderá usar o index zero no array _16b, justamente pelo motivo de que este irá precisar de dois _8b na sua construção, como expliquei a pouco.

Então ficamos com o index um do array _8b vago, mas a partir do index dois do array _8b começamos a armazenar os valores de posição do mouse. Já que o index dois será referente ao index um do array _16b, na linha 196, referenciamos este index, assim as coisas ficam separadas de uma forma lógica e funcional. A linha 197 segue o mesmo princípio de indexação. Mas você pode pensar que estamos ocupado todos os bits, mas na verdade, isto de fato não está acontecendo, temos os bits do index 3 do array _16b totalmente livres, assim como o index 1 do array _8b também livre, isto fora os 6 bits que também estarão livres dentro das duas SHORT que estamos usando para armazenar as posições X e Y. Mas como o sistema está nos passando tudo o que precisamos, estou apenas informando o que de fato temos em termos de uso ou não, isto para quem desejar fazer um outro tipo de uso para tais bits.

Você pode entender isto vendo a imagem abaixo.

Image

Nesta imagem você pode observar o conteúdo do que estará no buffer, byte a byte. Lembrando que cada byte representa 8 bits. Note que onde estamos pintando de azul estará sendo ocupado por alguma informação, já as áreas em branco se encontram livre, podendo receber algum outro tipo de dado ou informação futura. O X é onde estamos armazenando o conteúdo que representa a coordenada gráfica de X, a mesma coisa se aplica ao Y. Acredito que assim, vendo esta imagem, você irá compreender melhor o que estou de fato fazendo.

Com relação ao restante do código do indicador de mouse, praticamente não será necessário dar grandes explicações sobre o mesmo, já que o código em si será bastante simples de ser compreendido. No entanto, já que ele também passou por modificações, e quero deixar registrado o novo código. Veja como o mesmo deverá ser para que as coisas funcionem corretamente.

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

Código fonte do arquivo C_Study.mqh

Note que a classe C_Study praticamente não sofreu nenhuma grande mudança. Então não vejo motivo para mais explicações desta. Mas vamos ver o código do indicador em si, que pode ser observado logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "This is an indicator for graphical studies using the mouse."
04. #property description "This is an integral part of the Replay / Simulator system."
05. #property description "However it can be used in the real market."
06. #property version "1.56"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/pt/articles/12000"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. #property indicator_buffers 1
12. //+------------------------------------------------------------------+
13. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
14. //+------------------------------------------------------------------+
15. C_Study *Study       = NULL;
16. //+------------------------------------------------------------------+
17. input long   user00   = 0;                                //ID
18. input C_Study::eStatusMarket user01 = C_Study::eAuction;  //Market Status
19. input color user02   = clrBlack;                          //Price Line
20. input color user03   = clrPaleGreen;                      //Positive Study
21. input color user04   = clrLightCoral;                     //Negative Study
22. //+------------------------------------------------------------------+
23. C_Study::eStatusMarket m_Status;
24. int m_posBuff = 0;
25. double m_Buff[];
26. //+------------------------------------------------------------------+
27. int OnInit()
28. {
29.    ResetLastError();
30.    Study = new C_Study(user00, "Indicator Mouse Study", user02, user03, user04);
31.    if (_LastError != ERR_SUCCESS) return INIT_FAILED;
32.    if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
33.    {
34.       MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
35.       OnBookEvent((*Study).GetInfoTerminal().szSymbol);
36.       m_Status = C_Study::eCloseMarket;
37.    }else
38.       m_Status = user01;
39.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
40.    ArrayInitialize(m_Buff, EMPTY_VALUE);
41.    
42.    return INIT_SUCCEEDED;
43. }
44. //+------------------------------------------------------------------+
45. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
46. {
47.    m_posBuff = rates_total;
48.    (*Study).Update(m_Status);   
49.    
50.    return rates_total;
51. }
52. //+------------------------------------------------------------------+
53. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
54. {
55.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
56.    (*Study).SetBuffer(m_posBuff, m_Buff);
57.    
58.    ChartRedraw((*Study).GetInfoTerminal().ID);
59. }
60. //+------------------------------------------------------------------+
61. void OnBookEvent(const string &symbol)
62. {
63.    MqlBookInfo book[];
64.    C_Study::eStatusMarket loc = m_Status;
65.    
66.    if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
67.    MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
68.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : C_Study::eInTrading);
69.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
70.       if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
71.    if (loc != m_Status) (*Study).Update(m_Status);
72. }
73. //+------------------------------------------------------------------+
74. void OnDeinit(const int reason)
75. {
76.    if (reason != REASON_INITFAILED)
77.    {
78.       if ((*Study).GetInfoTerminal().szSymbol != def_SymbolReplay)
79.          MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
80.    }
81.    delete Study;
82. }
83. //+------------------------------------------------------------------+

Código fonte do indicador de Mouse

Note que aqui temos algumas diferenças frente ao que existia antes. Tais diferenças não são assim tão dramáticas a ponto de impedir que você consiga de fato entender o código. Mas existe dois pontos que acredito merecer um breve comentário. Estes pontos são as linhas 47 e 56.

Pois bem, na linha 47, você pode notar que diferente do que era feito antes, agora apenas armazenamos o valor que o MetaTrader 5 estará nos informando, não fazemos nenhuma mudança no mesmo, pois isto será feito em outro local.

Já na linha 56, repassamos as informações para a classe C_Mouse, onde iremos de fato ajustar as coisas, e gravar no buffer do indicador. Note que aqui a coisa toda ficou extremamente mais simples, justamente pelo fato de que estamos jogando toda a complicação para dentro da classe.


Conclusão

Com estas modificações, e adaptações do código, passamos a ter a possibilidade de realmente fazer uso do indicador de mouse no sistema de replay / simulador que estamos desenvolvendo. Já que agora podemos finalmente usar o sistema de envio de mensagens entre as aplicações envolvidas, ao mesmo tempo que poderemos fazer uso da leitura do buffer a fim de transferir informações entre as aplicações. A única limitação que de fato temos é que deveremos sempre usar uma única posição de buffer. Isto para não sobrecarregar o sistema de eventos. Mas isto será assunto para o futuro.

No vídeo abaixo, você pode ver a demonstração do sistema em funcionamento. No anexo você terá acesso aos códigos já compilados, a fim de poder fazer os testes e entender o que acontece de fato. Antes que eu me esqueça de mencionar, você pode verificar a falha de range, trocando o indicador de mouse que estará neste anexo, pelo que se encontra no artigo anterior. E rodar o serviço deste artigo. Ao fazer isto você irá ver a tal falha se manifestar.

Um último detalhe: Apesar de não constar neste artigo, o código do serviço que estará sendo utilizado, irei deixar para explicar o mesmo no próximo artigo, já que iremos usar este mesmo código como um trampolim para o que realmente estamos desenvolvendo. Então tenham um pouco de paciência e aguarde o próximo artigo, pois a coisa está ficando cada dia mais interessante.


Vídeo de demonstração


Arquivos anexados |
Anexo.zip (420.65 KB)
Balanceando riscos ao negociar múltiplos instrumentos simultaneamente Balanceando riscos ao negociar múltiplos instrumentos simultaneamente
Este artigo permitirá que um iniciante escreva uma implementação de um script do zero para balancear riscos ao negociar múltiplos instrumentos simultaneamente. Além disso, pode dar aos usuários experientes novas ideias para implementar suas soluções em relação às opções propostas neste artigo.
Introdução ao MQL5 (Parte 4): Estruturas, classes e funções de tempo Introdução ao MQL5 (Parte 4): Estruturas, classes e funções de tempo
Nesta série, continuamos a desvendar os segredos da programação. No novo artigo, vamos estudar as bases das estruturas, classes e funções de tempo e adquirir novas habilidades para programação eficiente. Este guia pode ser útil não apenas para iniciantes, mas também para desenvolvedores experientes, pois simplifica conceitos complexos, fornecendo informações valiosas para dominar o MQL5. Continue aprendendo coisas novas, aperfeiçoe suas habilidades de programação e domine o mundo da negociação algorítmica.
Construindo e testando sistemas de negociação com o Canal Keltner Construindo e testando sistemas de negociação com o Canal Keltner
Neste artigo, tentaremos fornecer sistemas de negociação usando um conceito muito importante no mercado financeiro, que é a volatilidade. Forneceremos um sistema de negociação baseado no indicador Canal Keltner após compreendê-lo e como podemos codificá-lo e criar um sistema de negociação baseado em uma estratégia simples de negociação e testá-lo em diferentes ativos.
Desenvolvendo um EA multimoeda (Parte 3): Revisão da arquitetura Desenvolvendo um EA multimoeda (Parte 3): Revisão da arquitetura
Nós já avançamos um pouco no desenvolvimento de um EA multimoeda com várias estratégias funcionando em paralelo. Com base na experiência acumulada, vamos revisar a arquitetura da nossa solução e tentar melhorá-la, antes que avancemos muito.