preview
Desenvolvendo um sistema de Replay (Parte 51): Complicando as coisas (III)

Desenvolvendo um sistema de Replay (Parte 51): Complicando as coisas (III)

MetaTrader 5Exemplos | 29 maio 2024, 10:05
88 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Desenvolvendo um sistema de Replay (Parte 50): Complicando as coisas (II), começamos a mudar mais profundamente, o indicador de controle, a ponto de conseguir fazer com que ele se mantivesse dentro do gráfico. Mas não qualquer gráfico e sim o que foi aberto pelo serviço de replay / simulação. Mas o principal, que foi de fato implementado lá, é a possibilidade de podermos usar um template pessoal, não mais sendo forçado a utilizar um que o sistema exigia.

O fato de permitir este tipo de coisa, torna o uso de todo o sistema de replay / simulação algo muito mais agradável e adequado, isto para quem de fato deseja fazer uso do sistema a fim de gerar algum tipo de estudo. Já que você pode criar um template, usar ele no sistema de replay / simulação e depois usar o mesmo template em uma conta real. Por conta disto o sistema está sofrendo as mudanças que estou mostrando.

No entanto, a interação entre o indicador de controle e o usuário, não tem estado muito adequado. Isto por conta, que o sistema tem alguns pontos duplicados. Mas o principal problema, é que o sistema não estava tão seguro e estável quando aparentava. Tudo isto por que durante a fase de desenvolvimento, que ocorreu na primeira fase, não houve a preocupação em relação ao fato de que alguns usuários, muitas das vezes tentam utilizar o sistema de uma maneira diferente da qual ele estava sendo projetado. Mas com a atual mudança de direção e posicionamento das coisas, o nível de segurança e estabilidade vai começar a se tornar adequado.

Apesar de que no momento, ainda estamos lidando com um sistema instável, no que se refere a interação com o usuário, isto logo será corrigido. Mas uma vez que esta correção seja feita, todo o sistema irá se beneficiar.

Se você leu, e aconselho que o faça, o artigo anterior, deve ter notado que começamos a usar o indicador de controle diretamente via serviço. Então este indicador deixou de estar disponível livremente para que o usuário o coloque em qualquer gráfico.

Mas, analisando com calma o código, notei que podemos fazer algumas mudanças de forma a tornar as coisas ainda mais adequadas. No entanto, será preciso fazer algumas mudanças em outros módulos que serão também usados pelo sistema de replay / simulação. Por conta disto este artigo será voltado para explicar as mudanças que acontecerão. Isto por que alguns destes módulos, poderão ser acessados pelo usuário. Desta forma é adequado, que você usuário deste sistema, e programador, que está acompanhando o desenrolar das coisas, compreenda o que pode ou não ser manipulado. Isto para evitar instabilidades no uso de qualquer um dos módulos. Lembre-se alguns módulos, poderão ser usados em conta REAL e conta DEMO, não apenas no sistema de replay / simulador.

Talvez você venha a imaginar que as mudanças que serão vistas aqui, não são tão grandes assim. Mas antes de promover novas mudanças temos que estabilizar o código. Por este motivo, talvez o artigo venha a se mostrar pouco focado. Mas volto a repetir: Você tem que entender como a coisa está sendo desenvolvida. Caso isto não seja corretamente compreendido, você não irá conseguir utilizar o sistema adequadamente.

Sem mais delongas, vamos começar as modificações.


Fazendo uso massivo dos módulos

A primeira das mudanças que será feita, é justamente no código fonte do indicador de controle. O código visto até artigo anterior, não fazia uso de alguns dos módulos que já foram projetados e desenvolvidos. Com isto tínhamos um certo disparate entre o que acontecia no indicador de controle e outros módulos do sistema. 

Um deste módulos é justamente o Indicador de mouse. Este modulo especificamente, foi desenvolvido para que pudesse acumular todo e qualquer assunto relacionado ao mouse. Assim qualquer outro código que viesse a ser desenvolvido, não precisaria se preocupar em fazer certos testes e analises. Isto tudo seria feito pelo modulo que estaria no indicador de mouse.

Tal indicador foi desenvolvido a algum tempo, nesta mesma sequência de artigos sobre o sistema de replay / simulação. No entanto, o mesmo havia sido concebido para trabalhar primeiramente com um sistema de conta real ou conta demo. Não sendo suficientemente adequado para trabalhar da forma como estamos fazendo agora. Mas isto será visto em breve em mais detalhes.

A forma de integrar o modulo presente no indicador de mouse e o indicador de controle pode ser visto no código abaixo, onde já foi feita algumas correções e mudanças a fim de garantir que tal integração aconteça.

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

Código fonte do Indicador de controle

Notem que este código é bem diferente em relação ao que existia antes. Mas a diferença mais gritante realmente se dá na função OnInit, onde na linha 22, temos uma declaração completamente diferente da que existia antes, para que a classe de controle fosse referenciada.

Você pode até ficar um pouco perdido ao olhar e tentar entender o que esta linha 22 está fazendo. Mas nesta linha estamos fazendo duas coisas:

  • A primeira, é tornando o indicador de controle um modulo, passivo de ser usado por qualquer outro modulo que necessitar olhar o que este indicador esteja fazendo;
  • A segunda coisa, é que estamos dizendo ao indicador de controle, que agora, não mais iremos usar uma codificação a fim de analisar o mouse. Ou seja, agora o indicador de controle irá perguntar ao indicador de mouse, o que o usuário está fazendo, ou pretendendo. Com base nestas informações, o indicador de controle irá efetivamente fazer o que é mais adequado.

Este tipo de mudança, ou decisão, de fazer com que o indicador de mouse, fique responsável por cuidar da interação entre o usuário, o mouse e o gráfico, é com certeza a melhor forma de fazer as coisas. Não faz sentido, duplicar as coisas, pois quando fazemos isto, corrigimos o problema no indicador de mouse, mas geramos problemas no indicador de controle, e quando colocamos os dois para trabalhar juntos, um fica atrapalhando o outro. Não queremos isto. Queremos que ambos trabalhem em harmonia e em perfeita sincronia. Desta maneira evitamos perca de tempo em corrigir as coisas, pois basta corrigir a forma como está interação está se dando.

Muito bem, mas se você prestar atenção irá notar que o indicador de controle, já não faz menção a classe C_Terminal, como era feito antes. Por que ?!?! O motivo é a herança. Decidi fazer com que a classe de controle, herda-se a classe C_Terminal. O motivo de fazer isto, é um pouco complicado de explicar neste momento. Mas isto se fez necessário por conta de algo que iremos fazer depois.

Dado esta primeira explicação sobre o código do indicador. Vamos dar uma olhada no código da classe de controle.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\Interprocess.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PathBMP           "Images\\Market Replay\\Control\\"
007. #define def_ButtonPlay        def_PathBMP + "Play.bmp"
008. #define def_ButtonPause       def_PathBMP + "Pause.bmp"
009. #define def_ButtonLeft        def_PathBMP + "Left.bmp"
010. #define def_ButtonLeftBlock   def_PathBMP + "Left_Block.bmp"
011. #define def_ButtonRight       def_PathBMP + "Right.bmp"
012. #define def_ButtonRightBlock  def_PathBMP + "Right_Block.bmp"
013. #define def_ButtonPin         def_PathBMP + "Pin.bmp"
014. #define def_ButtonWait        def_PathBMP + "Wait.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. #resource "\\" + def_ButtonWait
023. //+------------------------------------------------------------------+
024. #define def_PrefixObjectName    "Market Replay _ "
025. #define def_NameObjectsSlider   def_PrefixObjectName + "Slider"
026. #define def_PosXObjects         120
027. //+------------------------------------------------------------------+
028. #include "..\Auxiliar\C_Terminal.mqh"
029. #include "..\Auxiliar\C_Mouse.mqh"
030. //+------------------------------------------------------------------+
031. class C_Controls : private C_Terminal
032. {
033.    protected:
034.            enum EventCustom {ev_WaitOn, ev_WaitOff};
035.    private :
036. //+------------------------------------------------------------------+
037.            string  m_szBtnPlay;
038.            bool    m_bWait;
039.            struct st_00
040.            {
041.                    string  szBtnLeft,
042.                            szBtnRight,
043.                            szBtnPin,
044.                            szBarSlider,
045.                            szBarSliderBlock;
046.                    int     posPinSlider,
047.                            posY,
048.                            Minimal;
049.            }m_Slider;
050.            C_Mouse *m_MousePtr;
051. //+------------------------------------------------------------------+
052. inline void CreateObjectBitMap(int x, int y, string szName, string Resource1, string Resource2 = NULL)
053.                    {
054.                            ObjectCreate(GetInfoTerminal().ID, szName, OBJ_BITMAP_LABEL, 0, 0, 0);
055.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_XDISTANCE, def_PosXObjects + x);
056.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_YDISTANCE, y);
057.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_BMPFILE, 0, "::" + Resource1);
058.                            ObjectSetString(GetInfoTerminal().ID, szName, OBJPROP_BMPFILE, 1, "::" + (Resource2 == NULL ? Resource1 : Resource2));
059.                            ObjectSetInteger(GetInfoTerminal().ID, szName, OBJPROP_ZORDER, 1);
060.                    }
061. //+------------------------------------------------------------------+
062. inline void CreteBarSlider(int x, int size)
063.                    {
064.                            ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJ_RECTANGLE_LABEL, 0, 0, 0);
065.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
066.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Slider.posY - 4);
067.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
068.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
069.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
070.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
071.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
072.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
073.                            ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJ_RECTANGLE_LABEL, 0, 0, 0);
074.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
075.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Slider.posY - 9);
076.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
077.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
078.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
079.                    }
080. //+------------------------------------------------------------------+
081.            void CreateBtnPlayPause(bool state)
082.                    {
083.                            m_szBtnPlay = def_PrefixObjectName + "Play";
084.                            CreateObjectBitMap(0, 25, m_szBtnPlay, (m_bWait ? def_ButtonWait : def_ButtonPause), (m_bWait ? def_ButtonWait : def_ButtonPlay));
085.                            ObjectSetInteger(GetInfoTerminal().ID, m_szBtnPlay, OBJPROP_STATE, state);
086.                    }
087. //+------------------------------------------------------------------+
088.            void CreteCtrlSlider(void)
089.                    {
090.                            u_Interprocess Info;
091.                            
092.                            m_Slider.szBarSlider       = def_NameObjectsSlider + " Bar";
093.                            m_Slider.szBarSliderBlock  = def_NameObjectsSlider + " Bar Block";
094.                            m_Slider.szBtnLeft         = def_NameObjectsSlider + " BtnL";
095.                            m_Slider.szBtnRight        = def_NameObjectsSlider + " BtnR";
096.                            m_Slider.szBtnPin          = def_NameObjectsSlider + " BtnP";
097.                            m_Slider.posY = 40;
098.                            CreteBarSlider(77, 436);
099.                            CreateObjectBitMap(47, 25, m_Slider.szBtnLeft, def_ButtonLeft, def_ButtonLeftBlock);
100.                            CreateObjectBitMap(511, 25, m_Slider.szBtnRight, def_ButtonRight, def_ButtonRightBlock);
101.                            CreateObjectBitMap(0, m_Slider.posY, m_Slider.szBtnPin, def_ButtonPin);
102.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnPin, OBJPROP_ANCHOR, ANCHOR_CENTER);
103.                            if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
104.                            m_Slider.Minimal = Info.s_Infos.iPosShift;
105.                            PositionPinSlider(Info.s_Infos.iPosShift);
106.                    }
107. //+------------------------------------------------------------------+
108. inline void RemoveCtrlSlider(void)
109.                    {                       
110.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
111.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_NameObjectsSlider);
112.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
113.                    }
114. //+------------------------------------------------------------------+
115. inline void PositionPinSlider(int p, const int minimal = 0)
116.                    {
117.                            m_Slider.posPinSlider = (p < minimal ? minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
118.                            m_Slider.posPinSlider = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
119.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnPin, OBJPROP_XDISTANCE, m_Slider.posPinSlider + def_PosXObjects + 95);
120.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != minimal);
121.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != m_Slider.Minimal);
122.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBtnRight, OBJPROP_STATE, m_Slider.posPinSlider < def_MaxPosSlider);
123.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, minimal + 2);
124.                            ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
125.                            ChartRedraw(GetInfoTerminal().ID);
126.                    }
127. //+------------------------------------------------------------------+
128.    public  :
129. //+------------------------------------------------------------------+
130.            C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr)
131.                    :C_Terminal(Arg0),
132.                     m_bWait(false),
133.                     m_MousePtr(MousePtr)
134.                    {
135.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
136.                            m_szBtnPlay          = NULL;
137.                            m_Slider.szBarSlider = NULL;
138.                            m_Slider.szBtnPin    = NULL;
139.                            m_Slider.szBtnLeft   = NULL;
140.                            m_Slider.szBtnRight  = NULL;
141.                    }
142. //+------------------------------------------------------------------+
143.            ~C_Controls()
144.                    {
145.                            ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
146.                            ObjectsDeleteAll(GetInfoTerminal().ID, def_PrefixObjectName);
147.                    }
148. //+------------------------------------------------------------------+
149.            void Init(const bool state)
150.                    {
151.                            CreateBtnPlayPause(state);
152.                            GlobalVariableTemp(def_GlobalVariableReplay);
153.                            if (!state) CreteCtrlSlider();
154.                            ChartRedraw(GetInfoTerminal().ID);
155.                    }
156. //+------------------------------------------------------------------+
157.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
158.                    {
159.                            u_Interprocess Info;
160.                            
161.                            switch (id)
162.                            {
163.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOn):
164.                                            if (lparam == 0) break;
165.                                            m_bWait = true;
166.                                            CreateBtnPlayPause(true);
167.                                            break;
168.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOff):
169.                                            if (lparam == 0) break;
170.                                            m_bWait = false;
171.                                            Info.df_Value = dparam;
172.                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
173.                                            break;
174.                                    case CHARTEVENT_OBJECT_DELETE:
175.                                            if (StringSubstr(sparam, 0, StringLen(def_PrefixObjectName)) == def_PrefixObjectName)
176.                                            {
177.                                                    if (StringSubstr(sparam, 0, StringLen(def_NameObjectsSlider)) == def_NameObjectsSlider)
178.                                                    {
179.                                                            RemoveCtrlSlider();
180.                                                            CreteCtrlSlider();
181.                                                    }else
182.                                                    {
183.                                                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
184.                                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
185.                                                    }
186.                                                    ChartRedraw(GetInfoTerminal().ID);
187.                                            }
188.                                            break;
189.                            }
190.                    }
191. //+------------------------------------------------------------------+
192. };
193. //+------------------------------------------------------------------+
194. #undef def_PosXObjects
195. #undef def_ButtonPlay
196. #undef def_ButtonPause
197. #undef def_ButtonLeft
198. #undef def_ButtonRight
199. #undef def_ButtonPin
200. #undef def_NameObjectsSlider
201. #undef def_PrefixObjectName
202. #undef def_PathBMP
203. //+------------------------------------------------------------------+

Código fonte da classe C_Control

Talvez você não consiga de fato perceber grandes mudanças no código. De fato, elas são bem sutis. A primeira é o fato de que a classe de controle está herdando de forma privativa a classe C_Terminal. Isto pode ser visto na linha 31. Com isto, agora não teremos mais que usar um ponteiro, para acessar a classe C_Terminal. Ou seja, os procedimentos para acessar de forma massiva a classe C_Terminal, pode ser acessado diretamente, e em alguns casos, de forma até um pouco mais rápido. Isto por conta de alguns detalhes de compilação. Mas este tipo de coisa não vem ao caso.

A questão é que na linha 130, onde o constructor da classe é declarado, temos algo bastante curioso. Isto pensando no fato, de que apenas o serviço, de fato irá neste primeiro momento, acessar este indicador de controle. Observem a linha 135. Isto de fato não é tão necessário. Mas como é sempre adequado fazer o reuso do código, vamos garantir que o indicador siga as premissas que precisamos. Ou seja, apenas um único indicador por gráfico. Novamente, isto não seria de fato necessário, já que neste primeiro momento, apenas o serviço tem acesso a este indicador, e conseguirá colocar ele no gráfico. Existem outras formas de fazer isto, mas não irei entrar em detalhes, já que não quero motivar ninguém a fazer uso de um recurso sem de fato entender como ele funciona.

Uma outra coisa que merece ser mencionado neste constructor, é o fato de que na linha 133, estamos armazenando em uma variável global privativa da classe, um ponteiro para ser usado a fim de acessar o indicador de mouse. Mas isto será entendido depois.

Neste mesmo código, se você prestar atenção, irá notar que uma correção que aconteceu, mas assim como todo o código, é algo muito sutil, que, no entanto, muda muito as coisas. Não para o código, mas para o MetaTrader 5. É o fato das chamadas ChartRedraw receberem um valor. Normalmente não passamos nenhum valor para esta chamada. Mas por que estou fazendo isto agora ?!?! O motivo é o fato de que existe uma diferença no valor da ID do gráfico.

No artigo anterior, expliquei esta diferença. Mas talvez não tenha ficado claro o suficiente. Então vamos dar uma reforçada na ideia. O grande problema da ID do gráfico, não é quem irá de fato abrir o gráfico. Mas quem irá colocar as coisas nele. Vamos entender isto com calma, para entender por que precisamos as vezes passar um valor para a chamada ChartRedraw, e em outros momentos não precisamos fazer isto.

Quando o serviço abre o gráfico, mas poderia ser qualquer outra coisa, este gráfico irá receber uma ID pelo MetaTrader 5. Se você for verificar está ID, irá notar um dado valor, isto ao analisar o valor retornado por ChartOpen. Certo. Agora para colocar, por exemplo um indicador neste gráfico, o programa deverá usar esta mesma ID, que estará sendo retornada pela função ChartOpen. Até aí tudo tranquilo. Mas é exatamente neste ponto mora o problema.

Quando você, usuário, coloca o mesmo indicador no gráfico. E no mesmo gráfico, não irá receber a mesma ID, que o programa que usou ChartOpen recebeu. Bem, agora ficou tudo muito confuso. Você deve estar achando que sou louco, ou que não sei o que estou dizendo. Mas é isto mesmo. Quando o serviço abre o gráfico, ele irá receber, via ChartOpen um valor de ID, você deverá usar este valor para criar um handle de indicador, e assim conseguir colocar o indicador no gráfico correto. Se o indicador, como é o caso do indicador de controle, que faz uso de funções a fim de colocar objetos no gráfico, via chamada ObjectCreate, você precisará fornecer uma ID, para que o MetaTrader 5 saiba qual é o gráfico correto.

A ID que deverá ser fornecida, NÃO e repito, NÃO deverá ser, caso o gráfico tenha sido aberto por ChartOpen, a ID obtida por ChartID. Se você usar a ID de gráfico em uma chamada ObjectCreate, fornecida pela chamada ChartID, em um gráfico aberto por ChartOpen, o objeto não irá ser apresentado no gráfico. Mas se este mesmo código, que usa a função ObjectCreate, for colocada no gráfico, via usuário, ou por meio de um template, você deverá usar como ID o valor fornecido por ChartID.

Sei que isto parece muito confuso, e extremamente desorientador. Mas é justamente o que deve ser feito. Por conta disto, que a classe C_Terminal, sofreu a atualização a alguns artigos atras, justamente para que ela conseguisse lidar com isto.

Mas já que nosso código, não sabe, exatamente quem foi que o lançou no gráfico. Fazemos uso de uma chamada a classe C_Terminal, a fim de que a ID correta seja retornada. Assim a função ChartRedraw, irá executar a atualização do gráfico corretamente, como esperado.

Apesar de toda esta complicação, você pode notar na linha 157, onde começa o tratador de mensagens, que é o código responsável por tratar dos eventos informados pelo MetaTrader 5, os eventos que analisam o movimento do mouse e eventos de clique foram removidos. Isto por conta que iremos fazer este tipo de tratamento de uma forma um pouco diferente. Porém nesta mesma função. 

Acontece que antes de que este tratamento seja de fato feito, precisamos fazer mais algumas mudanças. Mas agora, não neste código do indicador, ou do serviço. Vamos ter que voltar ao código do indicador de mouse, e fazer algumas mudanças lá.


Atualizando o código do Indicador de Mouse

Esta atualização que iremos fazer agora, nada irá contra o que já é usado no indicador de mouse. Precisamos de fazer esta atualização, justamente pelo motivo visto acima. Já que no momento em que formos fazer uso do serviço de replay / simulação, este irá lançar no gráfico o indicador de mouse, a fim de que possamos interagir com o sistema de replay / simulação. Mas você não precisa de fato se preocupar em relação a determinadas coisas. Já que o próprio código fará os devidos ajustes e acertos, a fim de garantir que o indicador de mouse, venha a funcionar da mesma forma como funcionaria caso você, usuário o coloca-se manualmente no gráfico, ou fazendo uso de um template.

O grande problema de fato, se dá quando usamos os meios que estamos utilizando para inicializar corretamente o sistema de replay / simulação. Então a primeira das coisas que podemos ver e que foram atualizadas é o código fonte do indicador de mouse. Este pode ser visto na integra 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.51"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/pt/articles/11877"
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 - 4;
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.     SetBuffer();
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.             delete Study;
81.     }
82. }
83. //+------------------------------------------------------------------+
84. inline void SetBuffer(void)
85. {
86.     uCast_Double Info;
87.     
88.     m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
89.     m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
90.     Info._datetime = (*Study).GetInfoMouse().Position.dt;
91.     m_Buff[m_posBuff + 1] = Info.dValue;
92.     Info._int[0] = (*Study).GetInfoMouse().Position.X;
93.     Info._int[1] = (*Study).GetInfoMouse().Position.Y;
94.     m_Buff[m_posBuff + 2] = Info.dValue;
95.     Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
96.     m_Buff[m_posBuff + 3] = Info.dValue;
97. }
98. //+------------------------------------------------------------------+

Código fonte do Indicador Mouse

Atenção: Apesar de você imaginar que não existem diferenças entre este código, e o que foi visto no passado. Isto não é um fato. Existem diferenças. E você deve tomar o máximo de cuidado com elas.

Para o usuário, a principal diferença se dá na linha 17. Quero enfatizar que esta é talvez a linha mais complicada de todo o código. O ideal seria que o usuário, NUNCA pudesse de fato manipular, ou ver esta linha na interface do indicador. Isto por conta que todo o usuário iniciante irá ficar tentado em mexer nesta linha. Já que se trata de um input. No entanto você que está lendo este artigo, irá saber, que NUNCA, mas NUNCA mesmo, deverá mudar este valor. Este valor deverá ser ajustado, ou pelo programa que chama o indicador, ou pelo próprio indicador. Mas você usuário, volto a repetir, NUNCA deverá mudar este valor. Os demais valores podem ser ajustados e manipulados pelo usuário sem problemas, mas o valor de ID NUNCA.

Muito bem, além deste fato, a próxima coisa que chama atenção é o conteúdo da linha 30. Ali inicializamos o ponteiro da classe de estudos. Antes o constructor recebia 3 valores, que basicamente era as cores a serem utilizadas no indicador de mouse. Mas agora o constructor irá receber mais dois valores extras. O primeiro valor é o ID do gráfico, e o segundo valor é o nome do indicador. Este nome será usado, para podermos acessar o buffer do indicador depois. Então sempre que o indicador de mouse estiver no gráfico, e você desejar ler o buffer do mesmo, use este nome que está sendo indicado aqui.

Existem algumas outras poucas diferenças, no código, mas já que elas não são complicadas de entender, não irei entrar em detalhes. Então podemos passar para o código da classe C_Study. Este pode ser visto na integra, logo abaixo, e o motivo é que não estaremos mais fazendo uso de um ponteiro para acessar a classe C_Terminal. Iremos fazer uso da herança.

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

Código fonte da classe C_Study

Basicamente existe apenas uma única diferença entre este código acima, e o código antigo. Esta diferença é o fato de que estamos buscando a ID do gráfico diretamente na classe C_Terminal. Antes fazíamos uso de um ponteiro para isto. Agora usamos a herança. Mas de onde vem esta herança ?!?! Esta está vindo da classe C_Mouse. Então vamos ver o código da classe C_Mouse para entender melhor esta história de herança.

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

Código fonte da classe C_Mouse

Da mesma forma como veio acontecendo, aqui a grande diferença entre este código e o antigo, é o fato de não usarmos mais um ponteiro para acessar a classe C_Terminal. O responsável por isto está na linha 13, onde fazemos a herança da classe C_Terminal para a classe C_Mouse. Desta forma todos os procedimentos e funções públicas da classe C_Terminal, passam a estender a classe C_Mouse. Todo o código basicamente já foi explicado nos artigos sobre o Mouse. 

O último artigo representativo foi Desenvolvendo um sistema de Replay (Parte 31): Projeto Expert Advisor - Classe C_Mouse (V), você pode usar este artigo para conseguir alcançar o primeiro artigo sobre este indicador de mouse, já que existe um link no início do artigo, assim como acontece em todos os meus artigos, a fim de você poder voltar e acompanhar a evolução no desenvolvimento do sistema.

Se você tiver alguma dúvida de como o indicador de mouse funciona, ou deseja adaptá-lo as suas necessidades pessoais. Volte naquele ponto, onde expliquei como o indicador foi desenvolvido. Procure entender como ele funciona, depois retorne neste ponto, e adicione as modificações que estão sendo mostradas. Junto é claro, com as coisas que você precisa e se interessa em colocar no indicador de mouse. Se você fizer tudo corretamente e com calma, irá poder usar o seu próprio indicador de mouse, neste sistema de replay / simulador que estou mostrando como desenvolver.

Fica aí a dica para que deseja de fato aprender a programar. Tente adicionar alguma nova funcionalidade ao indicador de mouse e use isto no sistema de replay / simulador. Mas não se esqueça de indicar a fonte de seu conhecimento. Isto me traria muita satisfação. Além de me motivar, a sempre mostrar como faço para conseguir produzir as soluções, para problemas que muitos consideram ser insuperáveis.

Mas vamos voltar a nossa explicação do código. No constructor da classe C_Mouse, você pode notar que na linha 125, fazemos a inicialização da classe C_Terminal. O valor a ser usado na inicialização é informado lá no código do indicador, isto via um input que tem o nome de user00. Ok, se o usuário, deixar esta input em paz, não mexendo nela. A classe C_Terminal irá determinar qual é o ID do gráfico, e passará a usar está ID, onde for necessário para poder colocar os objetos no gráfico correto.

Além deste fato, temos também na linha 127, o travamento do indicador, a fim de evitar que ele seja colocado mais de uma vez no gráfico. Este tipo de coisa, irá evitar que o usuário venha a colocar um outro indicador mouse no gráfico, isto por conta que o usuário irá ter acesso a este indicador. Ele estará presente na lista de indicadores do MetaTrader 5.

Todo o restante do código funciona da mesma forma como era antes. Mas antes que você comece a perguntar, algumas coisas ao olhar o código dos próximos módulos. Este indicador mouse, é que irá dizer ao MetaTrader 5, para ligar eventos de mouse e os enviar ao gráfico onde o indicador se encontra. Atenção a isto: Você deverá ter o indicador de mouse no gráfico, para receber eventos de mouse do MetaTrader 5, não adianta tentar acessar o indicador de outro gráfico. Pois apesar de você, ter a possibilidade de ler o que o indicador de mouse, presente em outro gráfico está fazendo. O MetaTrader 5, não irá gerar eventos de mouse, para o gráfico que não contenha tal indicador.

Quem é responsável por fazer isto, é a linha 131. Então sempre que o indicador de mouse estiver no gráfico, qualquer EA ou outro indicador, irá receber eventos de mouse. E isto irá se manter, enquanto o indicador de mouse estiver no gráfico. Lembre-se deste fato, pois nos próximos código que serão vistos, irão precisar que o indicador de mouse, esteja presente no gráfico, para que os eventos relacionados ao mouse sejam disparados pelo MetaTrader 5, para os demais códigos que irão executar o tratamento destes eventos, de uma determinada forma bem especifica.

Antes de partimos para a etapa de modificar o indicador de controle. Acho valido, dar uma explicação um pouco melhor, do que está de fato acontecendo. Isto para que todo o conteúdo visto neste artigo, de fato fique bem assimilado.


Compreendendo e assimilando o conhecimento

Você deve estar achando que tudo que falei neste artigo, é bobagem, já que muitos já devem ter uma vasta experiência em MQL5, ou conhece um amigo que é fera neste sistema de codificação. Mas para deixar as coisas bem claras. Irei no anexo deste artigo, postar o executável do indicador de mouse. Isto para ser mais simples, no entanto você pode usar o conteúdo desta sequência e irá obter o mesmo código, já que estou colocando o código, na integra. Isto dentro dos artigos. Assim você poderá testar as coisas, e ver o que acontece ao usar o seguinte código, que é consideravelmente mais simples do que o sistema de replay / simulação, no entanto funciona seguindo os mesmos princípios.

01. //+------------------------------------------------------------------+
02. #property service
03. #property copyright "Daniel Jose"
04. #property version   "1.00"
05. //+------------------------------------------------------------------+
06. input string user00 = "EURUSD"; // Symbol
07. //+------------------------------------------------------------------+
08. void OnStart()
09. {
10.    long id;
11.    int handle;
12.    
13.    SymbolSelect(user00, true);
14.    id = ChartOpen(user00, PERIOD_M5);
15.    handle = iCustom(ChartSymbol(id), ChartPeriod(id), "\\Indicators\\Replay\\Mouse Study.ex5", id);
16.    ChartIndicatorAdd(id, 0, handle);
17.    IndicatorRelease(handle);
18. 
19.    Print("ID: ", id);
20.     
21.    while ((!_StopFlag) && (ChartSymbol(id) == user00)) Sleep(300);
22.     
23.    ChartClose(id);
24.    SymbolSelect(user00, false);    
25. }
26. //+------------------------------------------------------------------+

Código fonte do serviço de teste

Na linha 06, damos a possibilidade, ao usuário, de indicar um ativo. Este será usado para abrir o gráfico. Mas para abrir o gráfico, o símbolo deverá estar na janela de observação de mercado. Para fazer isto, usamos a linha 13. Já na linha 14, pedimos ao MetaTrader 5 para abrir o gráfico para nós. Na linha 15, criamos um handle que irá adicionar um indicador no gráfico, no caso iremos fazer com que o MetaTrader 5, carregue o Indicador de mouse. Na linha 16, adicionamos o indicador no gráfico. Já na linha 17, liberamos o handle, já que não precisamos mais dele. Na linha 19 imprimimos no terminal, uma mensagem que informa a ID do gráfico que foi aberto pelo serviço. Agora na linha 21 esperamos que o usuário feche o gráfico, ou encerre o serviço.

Caso o usuário feche o gráfico, a linha 23, não fará nada. No entanto, se o usuário encerrar o serviço, a linha 23 irá fechar o gráfico, que foi aberto antes. Já a linha 24 remove o ativo da observação de mercado. Um detalhe: Para que o ativo venha a ser removido, ele não deve ter nenhum tipo de coisa ligada a ele. No caso é apenas para testes, então ele não terá nada relacionado a ele.

Você pode estar imaginando que este código não faz muito sentido, por que eu me daria o trabalho de construir algo assim, e tentar explicar o funcionamento das coisas ?!?! Pois bem, é justamente isto que todos precisam de fato fazer. Procurar explorar além dos limites que muitos ficam confortavelmente explorando. Somente assim você realmente irá conseguir compreender como as coisas de fato funcionam.

Para compreender a ideia e essência do que estou querendo mostrar, já que isto é de suma importância para os próximos artigos. Você pode ver o vídeo 01 e observar como as coisas foram testadas, e testar você mesmo, usando seus próprios critérios. Mas devo ressaltar: Não aceite a verdade de alguém, apenas por que ele se acha melhor ou mais capaz. Teste você mesmo, questione a autoridade, pois somente assim o verdadeiro conhecimento será levado a luz.


Vídeo 01


Conclusão

Este artigo, foi um dos mais difíceis que achei até o momento de ser criado. Isto por conta do nível de complexidade envolvido nas informações que estão sendo passadas. Apesar da programação em si, vim a ser bem simples, isto ao meu ver. Explicar para você, caro leitor, o que acontece, é algo muito complicado. E o motivo é o fato de que pode haver pessoas com um nível de conhecimento bem maior, mas também pessoas e entusiastas que estão começando a tentar compreender este mundo chamado: PROGRAMAÇÃO.

No próximo artigo irei mostrar como fazer para gerar a interação entre o indicador de mouse, o indicador de controle, o serviço de replay / simulação e você, caro usuário do sistema. Então até breve.

Arquivos anexados |
Anexo.zip (420.65 KB)
Redes neurais de maneira fácil (Parte 68): Otimização off-line de políticas baseada em preferências Redes neurais de maneira fácil (Parte 68): Otimização off-line de políticas baseada em preferências
Desde os primeiros artigos sobre aprendizado por reforço, a gente sempre falou de duas coisas: como explorar o ambiente e definir a função de recompensa. Os artigos mais recentes foram dedicados à exploração durante o aprendizado off-line. Neste aqui, quero apresentar a você um algoritmo em que os autores resolveram deixar de lado a função de recompensa.
Algoritmos de otimização populacional: Mudamos a forma e deslocamos as distribuições de probabilidade e testamos com o "Cabeçudinho Inteligente" (Smart Cephalopod, SC) Algoritmos de otimização populacional: Mudamos a forma e deslocamos as distribuições de probabilidade e testamos com o "Cabeçudinho Inteligente" (Smart Cephalopod, SC)
Com este artigo investigaremos como a mudança de forma das distribuições de probabilidade afetam o desempenho dos algoritmos de otimização. Realizaremos experimentos baseados no algoritmo de teste "cabeçudinho inteligente" (Smart Cephalopod, SC) para avaliar o desempenho de diferentes distribuições de probabilidade no contexto de tarefas de otimização.
Funcionalidades do assistente MQL5 que você precisa conhecer (Parte 09): Combinação de agrupamento k-médias com ondas fractais Funcionalidades do assistente MQL5 que você precisa conhecer (Parte 09): Combinação de agrupamento k-médias com ondas fractais
O agrupamento k-médias é uma abordagem para agrupar pontos de dados em um processo que inicialmente se concentra na representação macro do conjunto de dados, onde são aplicados centroides de cluster criados aleatoriamente. Com o tempo, esses centroides são ajustados e escalonados para representar melhor o conjunto de dados. Este artigo examina essa abordagem de agrupamento e algumas de suas aplicações.
Filtragem e extração de características no domínio da frequência Filtragem e extração de características no domínio da frequência
Neste artigo, vamos explorar a aplicação de filtros digitais em séries temporais representadas no domínio da frequência, com o objetivo de extrair características únicas que podem ser úteis para modelos de previsão.