preview
Simulação de mercado (Parte 02): Cross Order (II)

Simulação de mercado (Parte 02): Cross Order (II)

MetaTrader 5Testador | 7 janeiro 2025, 09:53
335 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Simulação de mercado (Parte 01): Cross Order (I), mostrei e expliquei uma alternativa para um problema bastante comum, principalmente para quem opera, de alguma forma contratos futuros. Apesar de não ter mostrado a solução final, já que todo o artigo foi focado apenas no indicador Chart Trade. O conteúdo visto naquele artigo, é de suma importância. Já que ele nos dá a possibilidade, de fazer com que a classe C_Terminal, possa nos fornecer um nome adequado para operar os tais contratos futuros.

Porém, o problema ainda persiste. Não por conta do Chart Trade, ou por conta do Expert Advisor. Mas sim por conta do sistema de ordens pendentes. Que apesar de ainda não termos começado a falar sobre ele, aqui nos artigos. Temos que de alguma forma nos preparar para ele. Isto por que, parte das informações que serão usadas nele, vem do Chart Trade. E todo o processo de comunicação com o servidor, se dará via Expert Advisor. Ou seja, temos um triângulo, onde cada um dos vértices, representa uma das aplicações a serem desenvolvidas. Porém a aresta que se comunica com o servidor, sai do vértice do Expert Advisor.

Assim sendo, é perfeitamente plausível e aceitável, pensar em fazer o Expert Advisor controlar a escolha do contrato futuro. Isto para que possamos escolher se iremos em um dado momento operar, os minicontratos ou o contrato cheio. Porém, não podemos colocar a escolha em todas as aplicações. Ou seja, se a escolha de qual tipo de contrato a ser operado, deverá ser feito em apenas um único local. Isto para evitar ambiguidades nas escolhas ou na informação. O que por consequência, irá de fato tornar o sistema muito confuso para o usuário, caso a escolha do contrato pudesse ser feita em mais de um local.

Pois bem, ao fazer a escolha no Chart Trade, como foi mostrado no artigo anterior. Temos uma forma de controlar as coisas. No entanto, já que o Expert Advisor, é o único, que realmente se comunica diretamente com o servidor. Isto para envio de pedidos, podemos pensar em fazer a escolha nele. Mas isto nos traz um outro tipo de problema. E mostrar uma das formas de solucionar este problema, será o tema deste artigo. Mas antes que você, já imagine, que aqui vamos criar uma solução definitiva. Vou ressaltar que o que será visto aqui, é uma proposta para a solução. A solução mesmo será vista em um outro momento. Já que ainda não me decidi, como ela realmente se dará.


Entendendo os problemas

Já que a solução proposta aqui, irá de fato usar o Expert Advisor para selecionar qual o tipo de contrato a ser usado. Precisamos modificar algumas coisas no Chart Trade. Isto por que existe um problema, ao jogar a escolha do tipo de contrato, nas mãos do Expert Advisor. O problema em si, é a informação presente no Chart Trade. Esta informação pode ser vista na imagem abaixo.

Image 1

Observe que estou destacando, o nome do ativo, ou contrato. Um dos problemas é justamente este. Já que o nome mostrado ali, serve para calcular os valores a serem mostrados na parte financeira do Chart Trade. Então o problema se torna maior, pois o Chart Trade, envia para o Expert Advisor, os valores já convertidos em termos de ticks. Ficando assim o Expert Advisor, responsável apenas por enviar as ordens a mercado.

Pois bem, agora pense no problema. Se o Expert Advisor, mudar o tipo de contrato para minicontrato ou para o contrato cheio. O Chart Trade deverá replicar esta informação, para que o usuário ou operador não seja levado a cometer um erro. Replicar esta informação, não é nem de longe a parte complicada. Podemos fazer isto facilmente, fazendo com que o Chart Trade, se ligue ao Expert Advisor. Assim, diferente do que está sendo feito agora, onde o usuário, deverá colocar o Chart Trade no gráfico. Quem faria isto seria o Expert Advisor. Então o usuário iria de fato, colocar não o Chart Trade no gráfico, e sim o Expert Advisor. E este posteriormente lançaria o indicador Chart Trade no gráfico, já ajustando a informação do contrato.

Esta seria a solução mais fácil e óbvia. Porém isto faria com que fosse preciso, colocar o indicador Chart Trade em todos os Expert Advisores que fossem criados. Algo realmente e completamente inviável. Não pelo fato de se poder ou não fazer isto. Mas sim pelo fato, de que qualquer melhoria que fosse feita no Chart Trade, nos forçaria a ter que recompilar todos os Expert Advisors novamente. Por conta disto que foi decidido fazer o Chart Trade algo separado do Expert Advisor.

Muito bem, então vamos deixar as coisas separadas. Mas qual seria a forma de solucionar o problema neste caso? A solução para este tipo de caso, já vem sendo explicada, ela é usar troca de mensagens entre o Expert Advisor e o indicador Chart Trade. Simples não é mesmo? Bem, na verdade não é tão simples assim. Por isto decidi explicar como fazer isto. Por conta disto a necessidade deste artigo.

O primeiro problema que temos, é com relação a ordem em que as aplicações são colocadas no gráfico. Mas como assim? Bem, poderíamos forçar o usuário ou operador a colocar o Expert Advisor antes do Chart Trade. Assim quando o Chart Trade fosse entrar no gráfico, ele perguntaria ao Expert Advisor qual o contrato a ser usado. Legal, mas temos um outro problema, neste mesmo ponto. Podemos forçar o usuário a fazer as coisas, mas não podemos forçar o MetaTrader 5 a fazer isto. Talvez você não tenha pensando no seguinte problema. E este é o problema número dois. Quando o MetaTrader 5, tem que trocar o tempo do gráfico. Ele destrói o gráfico, abre ele novamente e logo em seguida recoloca o que havia no gráfico novamente. Isto em termos de indicadores e Expert Advisor. Pois bem, eis o problema: Quem entra primeiro. O Chart Trade ou o Expert Advisor? Se o Expert Advisor entra primeiro, ótimo, a solução do problema um resolve o problema dois. Mas se o Chart Trade entra primeiro, como ficamos?

Então como você pode ver a coisa é um pouco mais complicada do que parece. E além deste problema número dois, temos um terceiro problema. Se o usuário ou operador, mudar o parâmetro do contrato no Expert Advisor, ou qualquer outro parâmetro que venha a existir no Expert Advisor. O Chart Trade não será adequadamente informado, a todo o planejamento cobria apenas a solução para os problemas anteriores. Isto por que neste caso o Expert Advisor, seria removido do gráfico, e logo em seguida recolocado no gráfico pelo MetaTrader 5. Isto sem contar com o fato de que, precisamos de alguma forma fazer com que o Chart Trade, venha a ter consciência da presença do Expert Advisor. Caso contrário teremos outros problemas.

Felizmente, no caso da consciência, podemos deixar este passar. Isto por conta da questão de que, todo o sistema, tem sido pensado, de forma a fornecer informações para o usuário do que está se passando. Esta questão da consciência será um problema realmente sério, em um outro momento. Mas por hora, e aqui na relação entre o Chart Trade e o Expert Advisor, este não é um problema assim tão grave. Então para que não precisemos fazer uma mudança muito radical no código, vamos fazer algumas concessões. Mas para separar as coisas, vamos ver isto em um novo tópico.


Fazendo algumas concessões

As mudanças que iremos de fato fazer aqui, permanecerão como sendo feitas. Ou seja, o código não irá novamente regredir, mas sim avançar. Porém, tais concessões abrirão a possibilidade de fazer uso de determinadas coisas, que antes não eram possíveis. Sendo assim, faça uso destas novas possibilidades com atenção e cautela.

A primeira mudança pode ser vista no arquivo C_Terminal.mqh. Abaixo, você pode ver na íntegra o novo código a ser utilizado.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "Macros.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. class C_Terminal
008. {
009. //+------------------------------------------------------------------+
010.    protected:
011.       enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance};
012. //+------------------------------------------------------------------+
013.       struct st_Terminal
014.       {
015.          ENUM_SYMBOL_CHART_MODE   ChartMode;
016.          ENUM_ACCOUNT_MARGIN_MODE TypeAccount;
017.          long           ID;
018.          string         szSymbol;
019.          int            Width,
020.                         Height,
021.                         nDigits,
022.                         SubWin,
023.                         HeightBar;
024.          double         PointPerTick,
025.                         ValuePerPoint,
026.                         VolumeMinimal,
027.                         AdjustToTrade;
028.       };
029. //+------------------------------------------------------------------+
030.       void CurrentSymbol(bool bUsingFull)
031.          {
032.             MqlDateTime mdt1;
033.             string sz0, sz1;
034.             datetime dt = macroGetDate(TimeCurrent(mdt1));
035.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
036.       
037.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
038.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
039.             switch (eTS)
040.             {
041.                case DOL   :
042.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
043.                case IND   :
044.                case WIN   : sz1 = "GJMQVZ";       break;
045.                default    : return;
046.             }
047.             sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS)));
048.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
049.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
050.          }
051. //+------------------------------------------------------------------+
052.    private   :
053.       st_Terminal m_Infos;
054.       struct mem
055.       {
056.          long    Show_Descr,
057.                Show_Date;
058.          bool   AccountLock;
059.       }m_Mem;
060. //+------------------------------------------------------------------+
061. inline void ChartChange(void)
062.          {
063.             int x, y, t;
064.             m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
065.             m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
066.             ChartTimePriceToXY(m_Infos.ID, 0, 0, 0, x, t);
067.             ChartTimePriceToXY(m_Infos.ID, 0, 0, m_Infos.PointPerTick * 100, x, y);
068.             m_Infos.HeightBar = (int)((t - y) / 100);
069.          }
070. //+------------------------------------------------------------------+
071.    public   :
072. //+------------------------------------------------------------------+      
073.       C_Terminal(const long id = 0, const uchar sub = 0)
074.          {
075.             m_Infos.ID = (id == 0 ? ChartID() : id);
076.             m_Mem.AccountLock = false;
077.             m_Infos.SubWin = (int) sub;
078.             CurrentSymbol(false);
079.             m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
080.             m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
081.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
082.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true);
083.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true);
084.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
085.             m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
086.             m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
087.             m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
088.             m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
089.             m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
090.             m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
091.             m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
092.             m_Infos.ChartMode   = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
093.             if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
094.             ChartChange();
095.          }
096. //+------------------------------------------------------------------+
097.       ~C_Terminal()
098.          {
099.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date);
100.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr);
101.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, false);
102.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, false);
103.          }
104. //+------------------------------------------------------------------+
105. inline void SetTypeAccount(const ENUM_ACCOUNT_MARGIN_MODE arg)
106.          {
107.             if (m_Mem.AccountLock) return; else m_Mem.AccountLock = true;
108.             m_Infos.TypeAccount = (arg == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? arg : ACCOUNT_MARGIN_MODE_RETAIL_NETTING);
109.          }
110. //+------------------------------------------------------------------+
111. inline const st_Terminal GetInfoTerminal(void) const
112.          {
113.             return m_Infos;
114.          }
115. //+------------------------------------------------------------------+
116. const double AdjustPrice(const double arg) const
117.          {
118.             return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits);
119.          }
120. //+------------------------------------------------------------------+
121. inline datetime AdjustTime(const datetime arg)
122.          {
123.             int nSeconds= PeriodSeconds();
124.             datetime   dt = iTime(m_Infos.szSymbol, PERIOD_CURRENT, 0);
125.             
126.             return (dt < arg ? ((datetime)(arg / nSeconds) * nSeconds) : iTime(m_Infos.szSymbol, PERIOD_CURRENT, Bars(m_Infos.szSymbol, PERIOD_CURRENT, arg, dt)));
127.          }
128. //+------------------------------------------------------------------+
129. inline double FinanceToPoints(const double Finance, const uint Leverage)
130.          {
131.             double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1));
132.             
133.             return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade)));
134.          };
135. //+------------------------------------------------------------------+
136.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
137.          {
138.             static string st_str = "";
139.             
140.             switch (id)
141.             {
142.                case CHARTEVENT_CHART_CHANGE:
143.                   m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
144.                   m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
145.                   ChartChange();
146.                   break;
147.                case CHARTEVENT_OBJECT_CLICK:
148.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
149.                   if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true)
150.                      ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true);
151.                   break;
152.                case CHARTEVENT_OBJECT_CREATE:
153.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
154.                   st_str = sparam;
155.                   break;
156.             }
157.          }
158. //+------------------------------------------------------------------+
159. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const int zOrder = -1) const
160.          {
161.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false);
162.             ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0);
163.             ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n");
164.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false);
165.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor);
166.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false);
167.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false);
168.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder);
169.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true);
170.          }
171. //+------------------------------------------------------------------+
172.       bool IndicatorCheckPass(const string szShortName)
173.          {
174.             string szTmp = szShortName + "_TMP";
175.             
176.             IndicatorSetString(INDICATOR_SHORTNAME, szTmp);
177.             m_Infos.SubWin = ((m_Infos.SubWin = ChartWindowFind(m_Infos.ID, szTmp)) < 0 ? 0 : m_Infos.SubWin);
178.             if (ChartIndicatorGet(m_Infos.ID, m_Infos.SubWin, szShortName) != INVALID_HANDLE)
179.             {
180.                ChartIndicatorDelete(m_Infos.ID, 0, szTmp);
181.                Print("Only one instance is allowed...");
182.                SetUserError(C_Terminal::ERR_NoMoreInstance);
183.                
184.                return false;
185.             }
186.             IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
187.    
188.             return true;
189.          }
190. //+------------------------------------------------------------------+
191. };

Código fonte do arquivo C_Terminal.mqh

Observe que as mudanças foram bem poucas. Na verdade, do código visto no artigo anterior, apenas abrimos a possibilidade de chamar agora o procedimento CurrentSymbol dentro de outras classes que herdarem a classe C_Terminal. Anteriormente tal procedimento era privativo da classe, mas agora passei ele para o modo protegido. Muitos poderiam colocar ele diretamente como sendo público. Mas não gosto de mudar de forma tão radical assim. Prefiro dar o mínimo de privilegio, até que o programa se mostre com reais necessidades de se ter mais privilegio. Como um procedimento protegido ele, não poderá ser acessado de qualquer maneira.

Pois bem, além desta pequena mudança, foi feita uma outra. Agora no constructor, note que na linha 73, removi o parâmetro que foi adicionado no artigo anterior. Mas na linha 78 passei a forçar o uso inicial do minicontrato. Este tipo de coisa abre precedentes para se fazer novas coisas. E o motivo de ter feito assim, é justamente por conta que se a antiga estrutura fosse mantida, seriamos forçados a encerrar o Chart Trade a fim de atualizar o contrato a ser mostrado no indicador. Desta forma, não precisamos fazer mais isto. Podemos fazer com que uma mensagem permita a troca de contrato. Mas de qualquer forma, como consequência desta mudança teremos outras, que foram feitas no artigo anterior. Isto no que rege o código do Chart Trade. Mas por hora não vamos nos preocupar com isto. Precisamos fazer algo antes.

Em se falando em mensagens. Precisamos implementar novas mensagens para cobrir a solução dos problemas. Mas antes de ver as novas mensagens, vamos pensar um pouco. Observe a imagem abaixo:

Image 2

Aqui temos os pontos onde, a troca de mensagens irá de fato se dar. Note uma coisa, no caso do Expert Advisor, temos dois pontos ou dois procedimentos onde teremos envio de mensagens para o Chart Trade. E no caso do Chart Trade temos apenas um ponto.

Mas para entender como esta troca ocorrerá, é preciso que você entenda, que quando algo é colocado no gráfico, depois do OnInit, temos a execução do OnChartEvent. Porém, você sabe qual é o evento que é disparado, para que OnChartEvent seja executado, quando algo é colocado no gráfico? Bem, se você procurar saber isto, verá que quando algo é colocado no gráfico, seja um indicador ou expert advisor. O MetaTrader 5, dispara um evento CHARTEVENT_CHART_CHANGE. É muito importante saber disto. Pois, vamos usar justamente este evento, para fazer as coisas funcionarem.

Mas antes disto, é preciso você pensar no seguinte fato: Por que precisamos dos botões para enviar ordens, se o Expert Advisor que faz isto, pode não estar no gráfico? Não faz sentido. Então para mostrar que a troca de mensagens realmente ocorreu, e que a informação presente no Chart Trade pode e deve ser usada pelo usuário ou operador. Vamos mudar um pequeno, mas importante detalhe no Chart Trade. Então já temos as ideias que precisamos de fato, para poder implementar a solução do nosso problema.


Implementando a solução

A primeira coisa a ser feita, é adicionar três novos eventos ao nosso sistema. Estes podem ser visto no código do arquivo Defines.mqh, que é visto na íntegra 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. #define def_MaskTimeService      0xFED00000
16. #define def_IndicatorTimeFrame   (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96))))
17. #define def_IndexTimeFrame         4
18. //+------------------------------------------------------------------+
19. union uCast_Double
20. {
21.    double    dValue;
22.    long      _long;                                 // 1 Information
23.    datetime _datetime;                              // 1 Information
24.    uint     _32b[sizeof(double) / sizeof(uint)];    // 2 Informations
25.    ushort   _16b[sizeof(double) / sizeof(ushort)];  // 4 Informations
26.    uchar    _8b [sizeof(double) / sizeof(uchar)];   // 8 Informations
27. };
28. //+------------------------------------------------------------------+
29. enum EnumEvents    {
30.          evHideMouse,                  //Hide mouse price line
31.          evShowMouse,                  //Show mouse price line
32.          evHideBarTime,                //Hide bar time
33.          evShowBarTime,                //Show bar time
34.          evHideDailyVar,               //Hide daily variation
35.          evShowDailyVar,               //Show daily variation
36.          evHidePriceVar,               //Hide instantaneous variation
37.          evShowPriceVar,               //Show instantaneous variation
38.          evCtrlReplayInit,             //Initialize replay control
39.          evChartTradeBuy,              //Market buy event
40.          evChartTradeSell,             //Market sales event 
41.          evChartTradeCloseAll,         //Event to close positions
42.          evChartTrade_At_EA,           //Event to communication
43.          evEA_At_ChartTrade            //Event to communication
44.                   };
45. //+------------------------------------------------------------------+

Código fonte do arquivo Defines.mqh

O que foi adicionado aqui foram as linhas 42 e 43. Estas duas linhas, são quase que autoexplicativas, já que o segredo, está na direção da comunicação. Ou seja, a linha 42, diz respeito de uma comunicação partindo do Chart Trade para o Expert Advisor. Detalhe: Não confunda esta comunicação, com os eventos de negociação. Este evento daqui, será voltado para outro tipo de comunicação. Seria como se fosse um canal especial.

Já a linha 43, diz qual o nome do evento para resposta ao que o Chart Trade requisitou do Expert Advisor. No momento é isto, apenas estas duas novas linhas. Mas o resultado disto será enorme. Então vamos fazer o seguinte agora, vamos ver primeiramente o código do arquivo indicador Chart Trade. Este pode ser visto na íntegra logo abaixo. Note que ele está levemente diferente do que foi visto no artigo anterior. Ou seja, agora não temos mais aquele parâmetro que o usuário ou operador poderia ajustar.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Chart Trade Base Indicator."
04. #property description "See the articles for more details."
05. #property version   "1.81"
06. #property icon "/Images/Market Replay/Icons/Indicators.ico"
07. #property link "https://www.mql5.com/pt/articles/12537"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
12. //+------------------------------------------------------------------+
13. #define def_ShortName "Indicator Chart Trade"
14. //+------------------------------------------------------------------+
15. C_ChartFloatingRAD *chart = NULL;
16. //+------------------------------------------------------------------+
17. input ushort         user01 = 1;         //Leverage
18. input double         user02 = 100.1;     //Finance Take
19. input double         user03 = 75.4;      //Finance Stop
20. //+------------------------------------------------------------------+
21. int OnInit()
22. {
23.    chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03);
24.    
25.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
26. 
27.    return INIT_SUCCEEDED;
28. }
29. //+------------------------------------------------------------------+
30. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
31. {
32.    return rates_total;
33. }
34. //+------------------------------------------------------------------+
35. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
36. {
37.    if (_LastError < ERR_USER_ERROR_FIRST) 
38.       (*chart).DispatchMessage(id, lparam, dparam, sparam);
39. }
40. //+------------------------------------------------------------------+
41. void OnDeinit(const int reason)
42. {
43.    switch (reason)
44.    {
45.       case REASON_INITFAILED:
46.          ChartIndicatorDelete(ChartID(), 0, def_ShortName);
47.          break;
48.       case REASON_CHARTCHANGE:
49.          (*chart).SaveState();
50.          break;
51.    }
52. 
53.    delete chart;
54. }
55. //+------------------------------------------------------------------+

Código fonte do indicador Chart Trade

Bem, mas onde estão os novos eventos? Esperava os ver no procedimento OnChartEvent. Pois eles de fato estão ali, no procedimento OnChartEvent. No entanto, para simplificar as coisas, tudo está sendo tratado em um local. Ou seja, no procedimento DispatchMessage. E este procedimento está na classe C_ChartFloatingRAD que pode ser vista na íntegra logo abaixo.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Mouse.mqh"
005. #include "C_AdjustTemplate.mqh"
006. //+------------------------------------------------------------------+
007. #define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", GetInfoTerminal().ID, A)
008. #define macro_CloseIndicator(A)   {           \
009.                OnDeinit(REASON_INITFAILED);   \
010.                SetUserError(A);               \
011.                return;                        \
012.                                  }
013. //+------------------------------------------------------------------+
014. class C_ChartFloatingRAD : private C_Terminal
015. {
016.    private   :
017.       enum eObjectsIDE {MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL};
018.       struct st00
019.       {
020.          short    x, y, minx, miny,
021.                   Leverage;
022.          string   szObj_Chart,
023.                   szObj_Editable,
024.                   szFileNameTemplate;
025.          long     WinHandle;
026.          double   FinanceTake,
027.                   FinanceStop;
028.          bool     IsMaximized,
029.                   IsDayTrade,
030.                   IsSaveState;
031.          struct st01
032.          {
033.             short  x, y, w, h;
034.             color  bgcolor;
035.             int    FontSize;
036.             string FontName;
037.          }Regions[MSG_NULL];
038.       }m_Info;
039.       struct st01
040.       {
041.          short     y[2];
042.          bool      bOk;
043.       }m_Init;
044.       C_Mouse       *m_Mouse;
045. //+------------------------------------------------------------------+
046.       void CreateWindowRAD(int w, int h)
047.          {
048.             m_Info.szObj_Chart = "Chart Trade IDE";
049.             m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit";
050.             ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0);
051.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x);
052.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y);
053.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w);
054.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h);
055.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false);
056.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false);
057.             m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID);
058.          };
059. //+------------------------------------------------------------------+
060.       void AdjustEditabled(C_AdjustTemplate &Template, bool bArg)
061.          {
062.             for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_STOP_VALUE; c0++)
063.                if (bArg)
064.                {
065.                   Template.Add(EnumToString(c0), "bgcolor", NULL);
066.                   Template.Add(EnumToString(c0), "fontsz", NULL);
067.                   Template.Add(EnumToString(c0), "fontnm", NULL);
068.                }
069.                else
070.                {
071.                   m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor"));
072.                   m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz"));
073.                   m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm");
074.                }
075.          }
076. //+------------------------------------------------------------------+
077. inline void AdjustTemplate(const bool bFirst = false)
078.          {
079. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade
080.             
081.             C_AdjustTemplate    *Template;
082.             
083.             if (bFirst)
084.             {
085.                Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true);
086.                for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
087.                {
088.                   (*Template).Add(EnumToString(c0), "size_x", NULL);
089.                   (*Template).Add(EnumToString(c0), "size_y", NULL);
090.                   (*Template).Add(EnumToString(c0), "pos_x", NULL);
091.                   (*Template).Add(EnumToString(c0), "pos_y", NULL);
092.                }
093.                AdjustEditabled(Template, true);
094.             }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
095.             if (_LastError >= ERR_USER_ERROR_FIRST)
096.             {
097.                delete Template;
098.                
099.                return;
100.             }
101.             m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage);
102.             m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage));
103.             m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage));
104.             (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
105.             (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage));
106.             (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2));
107.             (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2));
108.             (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0"));
109.             (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
110.             if (!(*Template).Execute())
111.             {
112.                delete Template;
113. 
114.                macro_CloseIndicator(C_Terminal::ERR_FileAcess);
115.             };
116.             if (bFirst)
117.             {
118.                for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
119.                {
120.                   m_Info.Regions[c0].x = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_x"));
121.                   m_Info.Regions[c0].y = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_y"));
122.                   m_Info.Regions[c0].w = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_x"));
123.                   m_Info.Regions[c0].h = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_y"));
124.                }
125.                m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x;
126.                AdjustEditabled(Template, false);
127.             };
128.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? m_Init.y[m_Init.bOk] : m_Info.Regions[MSG_TITLE_IDE].h + 6));
129.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx));
130.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny));
131. 
132.             delete Template;
133.             
134.             ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
135.             ChartRedraw(m_Info.WinHandle);
136. 
137. #undef macro_PointsToFinance
138.          }
139. //+------------------------------------------------------------------+
140.       eObjectsIDE CheckMousePosition(const short x, const short y)
141.          {
142.             int xi, yi, xf, yf;
143.             
144.             for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
145.             {
146.                xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x;
147.                yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y;
148.                xf = xi + m_Info.Regions[c0].w;
149.                yf = yi + m_Info.Regions[c0].h;
150.                if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0;
151.             }
152.             return MSG_NULL;
153.          }
154. //+------------------------------------------------------------------+
155. inline void DeleteObjectEdit(void)
156.          {
157.             ChartRedraw();
158.             ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Editable);
159.          }
160. //+------------------------------------------------------------------+
161.       template <typename T >
162.       void CreateObjectEditable(eObjectsIDE arg, T value)
163.          {
164.             long id = GetInfoTerminal().ID;
165.             
166.             DeleteObjectEdit();
167.             CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0);
168.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3);
169.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3);
170.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w);
171.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h);
172.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor);
173.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER);
174.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1);
175.             ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName);
176.             ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value));
177.             ChartRedraw();
178.          }
179. //+------------------------------------------------------------------+
180.       bool RestoreState(void)
181.          {
182.             uCast_Double info;
183.             bool bRet;
184.             C_AdjustTemplate *Template;
185.             
186.             if (bRet = GlobalVariableGet(macro_NameGlobalVariable("POST"), info.dValue))
187.             {
188.                m_Info.x = (short) info._16b[0];
189.                m_Info.y = (short) info._16b[1];
190.                m_Info.minx = (short) info._16b[2];
191.                m_Info.miny = (short) info._16b[3];
192.                Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl");
193.                if (_LastError >= ERR_USER_ERROR_FIRST) bRet = false; else
194.                {
195.                   (*Template).Add("MSG_LEVERAGE_VALUE", "descr", NULL);
196.                   (*Template).Add("MSG_TAKE_VALUE", "descr", NULL);
197.                   (*Template).Add("MSG_STOP_VALUE", "descr", NULL);
198.                   (*Template).Add("MSG_DAY_TRADE", "state", NULL);
199.                   (*Template).Add("MSG_MAX_MIN", "state", NULL);
200.                   if (!(*Template).Execute()) bRet = false; else
201.                   {
202.                      m_Info.IsDayTrade = (bool) StringToInteger((*Template).Get("MSG_DAY_TRADE", "state")) == 1;
203.                      m_Info.IsMaximized = (bool) StringToInteger((*Template).Get("MSG_MAX_MIN", "state")) == 1;
204.                      m_Info.Leverage = (short)StringToInteger((*Template).Get("MSG_LEVERAGE_VALUE", "descr"));
205.                      m_Info.FinanceTake = (double) StringToDouble((*Template).Get("MSG_TAKE_VALUE", "descr"));
206.                      m_Info.FinanceStop = (double) StringToDouble((*Template).Get("MSG_STOP_VALUE", "descr"));
207.                   }
208.                };               
209.                delete Template;
210.             };
211.             
212.             GlobalVariablesDeleteAll(macro_NameGlobalVariable(""));
213.             
214.             return bRet;
215.          }
216. //+------------------------------------------------------------------+
217.    public   :
218. //+------------------------------------------------------------------+
219.       C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const short Leverage, const double FinanceTake, const double FinanceStop)
220.          :C_Terminal(0)
221.          {
222.             m_Mouse = MousePtr;
223.             m_Info.IsSaveState = false;
224.             if (!IndicatorCheckPass(szShortName)) return;
225.             if (!RestoreState())
226.             {
227.                m_Info.Leverage = Leverage;
228.                m_Info.IsDayTrade = true;
229.                m_Info.FinanceTake = FinanceTake;
230.                m_Info.FinanceStop = FinanceStop;
231.                m_Info.IsMaximized = true;
232.                m_Info.minx = m_Info.x = 115;
233.                m_Info.miny = m_Info.y = 64;
234.             }
235.             m_Init.y[false] = 150;
236.             m_Init.y[true] = 210;
237.             CreateWindowRAD(170, m_Init.y[m_Init.bOk = false]);
238.             AdjustTemplate(true);
239.          }
240. //+------------------------------------------------------------------+
241.       ~C_ChartFloatingRAD()
242.          {
243.             ChartRedraw();
244.             ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Chart);
245.             if (!m_Info.IsSaveState)
246.                FileDelete(m_Info.szFileNameTemplate);
247.                         
248.             delete m_Mouse;
249.          }
250. //+------------------------------------------------------------------+
251.       void SaveState(void)
252.          {
253. #define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B);
254.             
255.             uCast_Double info;
256.             
257.             info._16b[0] = m_Info.x;
258.             info._16b[1] = m_Info.y;
259.             info._16b[2] = m_Info.minx;
260.             info._16b[3] = m_Info.miny;
261.             macro_GlobalVariable(macro_NameGlobalVariable("POST"), info.dValue);
262.             m_Info.IsSaveState = true;
263.             
264. #undef macro_GlobalVariable
265.          }
266. //+------------------------------------------------------------------+
267.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
268.          {
269. #define macro_AdjustMinX(A, B)    {                          \
270.             B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x;   \
271.             mx = x - m_Info.Regions[MSG_TITLE_IDE].w;        \
272.             A = (B ? (mx > 0 ? mx : 0) : A);                 \
273.                                  }
274. #define macro_AdjustMinY(A, B)   {                           \
275.             B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y;   \
276.             my = y - m_Info.Regions[MSG_TITLE_IDE].h;        \
277.             A = (B ? (my > 0 ? my : 0) : A);                 \
278.                                  }
279.                               
280.             static short sx = -1, sy = -1, sz = -1;
281.             static eObjectsIDE obj = MSG_NULL;
282.             short   x, y, mx, my;
283.             double dvalue;
284.             bool b1, b2, b3, b4;
285.             ushort ev = evChartTradeCloseAll;
286.    
287.             switch (id)
288.             {
289.                case CHARTEVENT_CUSTOM + evEA_At_ChartTrade:
290.                   if (m_Init.bOk = ((lparam >= 0) && (lparam < 2)))
291.                      CurrentSymbol((bool)lparam);
292.                   AdjustTemplate(true);
293.                   break;
294.                case CHARTEVENT_CHART_CHANGE:
295.                   if (!m_Init.bOk)
296.                      EventChartCustom(GetInfoTerminal().ID, evChartTrade_At_EA, 0, 0, "");
297.                   x = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_WIDTH_IN_PIXELS);
298.                   y = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_HEIGHT_IN_PIXELS);
299.                   macro_AdjustMinX(m_Info.x, b1);
300.                   macro_AdjustMinY(m_Info.y, b2);
301.                   macro_AdjustMinX(m_Info.minx, b3);
302.                   macro_AdjustMinY(m_Info.miny, b4);
303.                   if (b1 || b2 || b3 || b4) AdjustTemplate();
304.                   break;
305.                case CHARTEVENT_MOUSE_MOVE:
306.                   if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft))
307.                   {                  
308.                      switch (CheckMousePosition(x = (short)lparam, y = (short)dparam))
309.                      {
310.                         case MSG_MAX_MIN:
311.                            if (sz < 0) m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
312.                            break;
313.                         case MSG_DAY_TRADE:
314.                            if ((m_Info.IsMaximized) && (sz < 0)) m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true);
315.                            break;
316.                         case MSG_LEVERAGE_VALUE:
317.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_LEVERAGE_VALUE, m_Info.Leverage);
318.                            break;
319.                         case MSG_TAKE_VALUE:
320.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_TAKE_VALUE, m_Info.FinanceTake);
321.                            break;
322.                         case MSG_STOP_VALUE:
323.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_STOP_VALUE, m_Info.FinanceStop);
324.                            break;
325.                         case MSG_TITLE_IDE:
326.                            if (sx < 0)
327.                            {
328.                               ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
329.                               sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx);
330.                               sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny);
331.                            }
332.                            if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx);
333.                            if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my);
334.                            if (m_Info.IsMaximized)
335.                            {
336.                               m_Info.x = (mx > 0 ? mx : m_Info.x);
337.                               m_Info.y = (my > 0 ? my : m_Info.y);
338.                            }else
339.                            {
340.                               m_Info.minx = (mx > 0 ? mx : m_Info.minx);
341.                               m_Info.miny = (my > 0 ? my : m_Info.miny);
342.                            }
343.                            break;
344.                         case MSG_BUY_MARKET:
345.                            ev = evChartTradeBuy;
346.                         case MSG_SELL_MARKET:
347.                            ev = (ev != evChartTradeBuy ? evChartTradeSell : ev);
348.                         case MSG_CLOSE_POSITION:
349.                            if ((m_Info.IsMaximized) && (sz < 0) && (m_Init.bOk)) //<<
350.                            {
351.                               string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'),
352.                                                           m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage));                           
353.                               PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp);
354.                               EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp);
355.                            }
356.                            break;
357.                      }
358.                      if (sz < 0)
359.                      {
360.                         sz = x;
361.                         AdjustTemplate();
362.                         if (obj == MSG_NULL) DeleteObjectEdit();
363.                      }
364.                   }else
365.                   {
366.                      sz = -1;
367.                      if (sx > 0)
368.                      {
369.                         ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                  
370.                         sx = sy = -1;
371.                      }
372.                   }
373.                   break;
374.                case CHARTEVENT_OBJECT_ENDEDIT:
375.                   switch (obj)
376.                   {
377.                      case MSG_LEVERAGE_VALUE:
378.                      case MSG_TAKE_VALUE:
379.                      case MSG_STOP_VALUE:
380.                         dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT));
381.                         if (obj == MSG_TAKE_VALUE)
382.                            m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue);
383.                         else if (obj == MSG_STOP_VALUE)
384.                            m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue);
385.                         else
386.                            m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue));
387.                         AdjustTemplate();
388.                         obj = MSG_NULL;
389.                         ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable);
390.                         break;
391.                   }
392.                   break;
393.                case CHARTEVENT_OBJECT_DELETE:
394.                   if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown);
395.                   break;
396.             }
397.             ChartRedraw();
398.          }
399. //+------------------------------------------------------------------+
400. };
401. //+------------------------------------------------------------------+
402. #undef macro_NameGlobalVariable
403. #undef macro_CloseIndicator
404. //+------------------------------------------------------------------+

Código fonte do arquivo C_ChartFloatingRAD.mqh

Apesar de este código do arquivo de cabeçalho C_ChartFloatingRAD.mqh, parecer enorme para muitos. E não necessitando ser visto na íntegra, decidi postar ele neste artigo na íntegra. Isto por conta das mudanças que ocorreram frente ao que foi visto no artigo anterior. Mas a única e principal mudança foi no constructor da classe. Mas como numeração das linhas mudaram, e não queria deixar você caro leitor, perdido tentando achar as linhas corretas. O código foi postado na íntegra.

Mas vamos ver o que realmente mudou. Não com relação ao artigo passado, e sim para dar suporte, ao que poderá ser visto no vídeo, que se encontra no final deste artigo. A primeira coisa é o surgimento de uma pequena estrutura na linha 39. Muita atenção ao que explicarei, caso contrário, você não entenderá o que é visto no vídeo. Dentro desta estrutura, temos a linha 41. Esta conterá dois valores para o tamanho da janela, que na verdade é um objeto OBJ_CHART. Não se preocupe, logo você entenderá isto. Assim como também a variável da linha 42. Que em conjunto com este array de duas unidades, promove o que pode ser visto no vídeo.

Vamos então passar para o constructor da classe, este se inicia na linha 219. Repare, que não temos o parâmetro extra, que foi visto no artigo anterior. Assim como também, a linha 220 onde chamamos o constructor da classe C_Terminal. Ou seja, neste caso voltamos ao ponto anterior ao que foi visto no artigo passado. Agora preste atenção ao seguinte: A janela do Chart Trade, é um objeto OBJ_CHART. Ela pode estar minimizada ou maximizada. Isto já se encontrava implementado. Porém, quando o Expert Advisor, não estiver no gráfico, onde o Chart Trade estiver, queremos que os botões sejam ocultados.

Para isto, precisamos mudar a dimensão na direção Y do sistema de coordenadas. Tais dimensões, são definidas neste momento, nas linhas 235 e 236. Agora preste muita atenção: Quando o valor da variável m_Init.bOk for falso, indica que algo não está batendo. Neste caso ocultamos os botões de interação, a fim de evitar que o usuário, ou operador, pense que está enviando ordens para o servidor. Para que a ocultação aconteça, o valor na coordenada Y deverá ser de 150. Quando o valor de m_Init.bOk for verdadeiro, significa que o usuário ou operador pode enviar ordens via Expert Advisor. Assim o valor da coordenada Y será de 210. Este valor de 210, era exatamente o valo default que existia antes na chamada vista na linha 237.

Observe que a linha 237, que é responsável por criar o objeto OBJ_CHART, para comportar o Chart Trade, agora está diferente. Note que no lugar de colocar o valor 210, como dimensão, estamos usando um acesso ao valor definido no array. Ao mesmo tempo, definimos o valor de m_Init.bOk como sendo falso. Ou seja, na verdade estamos mandando o objeto OBJ_CHART ser construído com o valor 150. Mas por que não usar este valor logo de cara? O motivo é testar e atestar que o código esteja realmente funcionando conforme é esperado. Se o valor 150, fosse passado, diferente da forma como está sendo feito, não teríamos a certeza de que a modelagem está funcionando. Mas desta maneira como está sendo feita, podemos garantir que esteja funcionando.

Agora um novo detalhe, e este está no procedimento AdjustTemplate, que é chamado logo depois, e cujo código é visto na linha 77. Neste procedimento, a única mudança que foi feita é na linha 128. Isto por conta do seguinte fato: Quando maximizamos ou minimizamos o Chart Trade, fazemos isto mudando o valor da coordenada Y. Então para que o Chart Trade, se mantenha consistente com aquilo que estamos vendo, precisamos fazer com que os valores da coordenada Y, siga as regras que informamos lá no constructor da classe. Desta forma, mesmo que você maximize ou minimize o Chart Trade, tentando ter acesso aos botões. Eles não serão de fato visíveis, até que tudo esteja de acordo para eles serem mostrados.

Esta parte foi fácil, veja que as mudanças foram bem simples e poucas. Agora vamos ver a parte das mensagens. Que também contém algumas poucas modificações. Então vamos para a linha 267 onde se inicia o procedimento DispatchMessage. Antes de vermos as mensagens, vamos para a linha 349, onde você pode notar uma diferença mínima, mas muito importante. Para não complicar, desnecessariamente a função de checagem do mouse, a fim de saber onde ocorreu o clique. Adicionei nesta linha 349, um novo valor a ser testado. Então caso o valor da variável m_Init.bOk, seja falso, e venhamos a clicar na região onde estaria os botões. O teste fará com que nenhum evento seja disparado. É este nível de simplicidade que torna a programação algo espetacular. Já que muitos iriam de fato tentar verificar isto em outro ponto do código, o que faria com que o procedimento de checagem, ficasse demasiadamente complicado.

Muito bem, mas vamos voltar agora a questão das mensagens. Vamos começar com o seguinte: Quando o indicador é colocado no gráfico, você já sabe, que o primeiro evento que será recebido é o CHARTEVENT_CHART_CHANGE, com isto na linha 295, verificamos se a nossa variável se encontra em estado falso. Se isto for confirmado, na linha 296 disparamos um evento customizado. De qualquer forma, seja por que o Expert Advisor capturou o evento disparado, ou o Expert Advisor acabou de ser colocado no gráfico, teremos como resultado um outro evento. Este é capturado pelo Chart Trade na linha 289.

Agora vem a parte realmente interessante. Quando o valor testado na linha 290, for maior ou igual a zero, teremos a execução da linha 291. Preste atenção ao seguinte fato. Se o valor for zero, significará falso, se for diferente de zero significa verdadeiro. Porém, caso o valor seja maior do que um, não deveremos aceitar ele. Assim sendo o Expert Advisor, deverá passar um valor igual a zero ou um. E isto indicará se estaremos usando um minicontrato ou um contrato cheio. No final, fazemos na linha 292 um pedido para que o Chart Trade seja atualizado. Falando assim, isto parece muito complicado. Ainda mais por que podemos ter um valor diferente de zero e um chegando. Se este for o caso, os botões deverão ser ocultados. Isto por que o Expert Advisor acabou de ser removido do gráfico, ou algo estranho acabou de ocorrer.

Agora você deve estar imaginando que eu sou algum tipo de maluco. Como assim? Como o Chart Trade recebeu uma mensagem dizendo que o Expert Advisor, foi removido do gráfico, ou que algo estranho aconteceu? Bom, para entender isto vamos ao tópico do Expert Advisor. Este é visto a seguir.


Como o Expert Advisor passou a funcionar?

Muito bem, o código do Expert Advisor, para a demonstração pode ser visto na íntegra logo abaixo.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Virtual Test..."
04. #property description "Demo version between interaction"
05. #property description "of Chart Trade and Expert Advisor"
06. #property version   "1.81"
07. #property link "https://www.mql5.com/pt/articles/12537"
08. //+------------------------------------------------------------------+
09. #include <Market Replay\Defines.mqh>
10. //+------------------------------------------------------------------+
11. class C_Decode
12. {
13.    private   :
14.       struct stInfoEvent
15.       {
16.          EnumEvents  ev;
17.          string      szSymbol,
18.                      szContract;
19.          bool        IsDayTrade;
20.          ushort      Leverange;
21.          double      PointsTake,
22.                      PointsStop;
23.       }info[1];
24.    public   :
25. //+------------------------------------------------------------------+
26.       C_Decode()
27.          {
28.             info[0].szSymbol = _Symbol;
29.          }
30. //+------------------------------------------------------------------+   
31.       bool Decode(const int id, const string sparam)
32.       {
33.          string Res[];
34.       
35.          if (StringSplit(sparam, '?', Res) != 7) return false;
36.          stInfoEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])};
37.          if ((id == loc.ev) && (loc.szSymbol == info[0].szSymbol)) info[0] = loc;
38.          
39.          ArrayPrint(info, 2);
40.       
41.          return true;
42.       }
43. }*GL_Decode;
44. //+------------------------------------------------------------------+
45. enum eTypeContract {MINI, FULL};
46. //+------------------------------------------------------------------+
47. input eTypeContract user00 = MINI;       //Cross order in contract
48. //+------------------------------------------------------------------+
49. bool bOk;
50. //+------------------------------------------------------------------+
51. int OnInit()
52. {
53.    bOk = false;
54.    GL_Decode = new C_Decode;
55.    
56.    return INIT_SUCCEEDED;
57. }
58. //+------------------------------------------------------------------+
59. void OnTick() {}
60. //+------------------------------------------------------------------+
61. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
62. {
63.    switch (id)
64.    {
65.       case CHARTEVENT_CUSTOM + evChartTradeBuy     :
66.       case CHARTEVENT_CUSTOM + evChartTradeSell    :
67.       case CHARTEVENT_CUSTOM + evChartTradeCloseAll:
68.          GL_Decode.Decode(id - CHARTEVENT_CUSTOM, sparam);
69.          break;
70.       case CHARTEVENT_CHART_CHANGE:
71.          if (bOk)   break;
72.       case CHARTEVENT_CUSTOM + evChartTrade_At_EA:
73.          bOk = true;
74.          EventChartCustom(ChartID(), evEA_At_ChartTrade, user00, 0, "");
75.          break;
76.    }
77. }
78. //+------------------------------------------------------------------+
79. void OnDeinit(const int reason)
80. {
81.    switch (reason)
82.    {
83.       case REASON_REMOVE:
84.       case REASON_INITFAILED:
85.          EventChartCustom(ChartID(), evEA_At_ChartTrade, -1, 0, "");
86.          break;
87.    }
88.    delete GL_Decode;
89. }
90. //+------------------------------------------------------------------+

Código fonte do Expert Advisor

Agora vem a pergunta: Como este código funciona? Bem, antes de responder isto, vamos ver uma pequena mudança que se deu. Isto no artigo anterior, quando passamos a fazer o Chart Trade indicar qual era o contrato que ele estava vendo. Se você olhar o código do Chart Trade, para ser mais preciso, o código da classe C_ChartFloatingRAD. Na linha 351, foi adicionado uma nova informação. Esta informação diz respeito ao nome do contrato. E está sendo decodificado na linha 36 do código do Expert Advisor. Este era um pequeno detalhe, que precisava ser mencionado. Agora vamos ver como este código do Expert Advisor funciona.

Muito bem, observe que o código que permitia ao usuário, ou operador, indicar o tipo de contrato. Que antes estava presente no Chart Trade, agora está nas linhas 45 e 47 do Expert Advisor. Porém em lugar nenhum acessamos a classe C_Terminal, neste Expert Advisor. Mas por que? O motivo é que ainda não estamos nos conectado ao servidor. O Expert Advisor ainda está na fase de demonstração. De qualquer maneira, observe que temos na linha 49, uma variável. Esta serve para evitar que qualquer mudança no gráfico, dispare um evento para o Chart Trade. A fim de dizer algo a ele. Na linha 53, inicializamos esta variável como falso. Já na linha 73, a declaramos como verdadeira. E na linha 71 a testamos a fim de evitar justamente este envio desnecessário de mensagens.

Agora preste atenção ao seguinte fato: Igual ao indicador, o Expert Advisor, tem como primeiro evento um CHARTEVENT_CHART_CHANGE, isto está declarando na linha 70. Já que a variável está como sendo falso. Iremos de fato executar a mesma coisa que seria feita, caso o Chart Trade requisitasse algo. Ou seja, caímos na linha 72. O resultado disto é que na linha 74, será disparado um evento, onde o valor indicado será zero ou um. Por isto que a enumeração na linha 45, deve seguir o que está sendo mostrado. Se você mudar a ordem dos termos, o resultado será diferente no Chart Trade. Só e somente mude as coisas aqui, se você souber o que está fazendo. Caso contrário, o Chart Trade irá lhe apresentar o contrato errado.

Agora vem a segunda parte, que é onde o Expert Advisor, diz ao Chart Trade que não poderá mais exercer seu papel. Precisando assim que o Chart Trade, oculte os botões de envio de ordens. Isto será feito, quando a rotina na linha 79 for executada. Em situações normais, o MetaTrader 5, dispara um evento DeInit para encerrar algo no gráfico. Quando isto acontece, o valor reason contém o motivo de tal chamada. Aqui na linha 81, verificamos tais motivos. Se eles forem o que estiverem sendo declarados, um evento será disparado na linha 85. Note que o valor depois do tipo de evento é -1. Isto para o Chart Trade, indicará que o Expert Advisor não estará mais disponível para uso. Desta forma, os controles de envio de ordens serão ocultados automaticamente.

O restante, do código permaneceu inalterado perto do que foi visto, no artigo onde expliquei sobre a comunicação com o Chart Trade. Sendo assim considero esta etapa concluída.


Considerações finais

Apesar de ter mostrado, como você pode implementar muito facilmente uma comunicação a fim de inicializar tanto o Expert Advisor, quando o indicador Chart Trade. Não sei neste momento, se o código visto neste artigo, realmente será visto e utilizado. Isto por conta de que, ainda existe a questão sobre a parte de ordens pendentes. Esta que realmente será a nossa pedra no sapato. Já que até aqui, o usuário ou operador pode enviar pedidos para o servidor, usando um minicontrato ou um contrato cheio. Isto somente pedindo para mudar um dos parâmetros do sistema. Tal tipo de coisa, não foi tão complicada de ser pensada e elaborada. Tudo que foi preciso fazer já havia sido exposto em outros artigos desta mesma sequência. Porém diferentemente de enviar mensagens entre aplicações, colocar informações sobre ordens pendentes, torna a história bem mais complicada.

De qualquer maneira, o resultado de tais mudanças até o presente momento, podem ser vistas no vídeo. E no anexo, você terá acesso aos executáveis usados no vídeo, para que possa entender o que foi feito.


Arquivos anexados |
Anexo.zip (490.53 KB)
Do básico ao intermediário: Template e Typename (I) Do básico ao intermediário: Template e Typename (I)
Aqui neste artigo, começaremos a lidar com um dos conceitos, que muitos iniciantes evitam. Isto por conta de que, templates, não é um assunto que podemos dizer, ser simples de entender e utilizar. Já que muitos não compreendem o princípio básico que se encontra por baixo do que seria um template. Que é justamente a sobrecarga de funções e procedimentos.
Simulação de mercado (Parte 01): Cross Order (I) Simulação de mercado (Parte 01): Cross Order (I)
Deste artigo em diante iniciaremos a fase dois, na questão sobre replay / simulação de mercado. Então aqui vamos começar mostrando uma possível solução para fazer cruzamento de ordens. Esta solução que mostrarei, não é uma solução definitiva. Ela é apenas uma proposta de solução para o problema que ainda será preciso abordar em breve.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Do básico ao intermediário: Sobrecarga Do básico ao intermediário: Sobrecarga
Este talvez será o artigo mais confuso para você iniciante. Já que aqui mostrarei que nem sempre, teremos em um mesmo código, todas funções e procedimentos com nomes exclusivos. Podemos sim ter funções e procedimentos com um mesmo nome e isto é conhecido como sobrecarga. O conteúdo exposto aqui, visa e tem como objetivo, pura e simplesmente a didática. De modo algum deve ser encarado como sendo, uma aplicação cuja finalidade não venha a ser o aprendizado e estudo dos conceitos mostrados.