preview
Simulação de mercado (Parte 08): Sockets (II)

Simulação de mercado (Parte 08): Sockets (II)

MetaTrader 5Exemplos |
151 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Simulação de mercado (Parte 07): Sockets (I), demonstrei os primeiros passos para que você possa começar a estudar soquetes. Porém, aquela aplicação vista ali, talvez não seja assim tão empolgante. Para dizer a verdade, aquilo ali estaria mais como um programa "HELLO WORLD". Que é o primeiro programa que procuramos fazer, quando começamos a estudar algo que envolve programação. Sendo assim um primeiro passo, para uma longa caminhada.

Ok, sendo assim, neste artigo mostrarei como fazer algo um pouco mais interessante. Para dizer a verdade, será algo não tão útil. Mas você poderá brincar e se divertir um pouco estudando os conceitos envolvidos. Assim, como foi feito no artigo anterior, iremos aqui também precisar de uma programação externa. Novamente, o motivo de usar uma programação externa, é por que não quero adicionar DLLs ao MetaTrader 5. Pelo menos por enquanto.

Então no que diz respeito ao código externo, não vou entrar em muitos detalhes de como ele funciona. Visto que você poderá encontrar, diversos códigos que executarão o mesmo tipo de tarefa. Ou poderá criar o seu próprio código estudando as referências que indiquei no artigo anterior. Mas o que vamos fazer neste artigo? Bem, neste artigo, para demonstrar como soquetes podem ser interessantes. E de fato desejo fazer algum uso deles no sistema de replay/simulador. Pelo menos estou planejando fazer isto. Iremos da maneira o mais simples quanto for possível fazer. Criar um Mini Chat. Isto mesmo. Vou mostrar como você pode criar um Mini Chat no MetaTrader 5.


Planejamento

O conceito por trás de um mini chat é bastante simples. E para ser sincero, até obvio. Você tem uma área onde pode digitar coisas, um botão para enviar e uma outra área para ver o que foi enviado por outras pessoas. Ou seja, algo bastante simples de ser feito. Teremos então de colocar um objeto de edição, um outro botão e mais um objeto para ler as mensagens postadas. Tudo isto, o MetaTrader 5 já nos oferece.

Muito bem. Normalmente tais programas de chat, costumam usar um sistema de cliente servidor embutido. Mas aqui, para usar o MQL5 puro, o servidor será um programa externo, e os clientes serão feitos no MQL5. Esta é a parte legal do uso de soquetes. Você não precisa se limitar a usar as coisas de uma forma única. Você pode as fazer de diversas maneiras diferentes. Mas você pode estar pensando: Como vamos controlar as conexões. A ponto de podermos colocar qualquer quantidade de participantes no nosso mini chat? Tal coisa deve ser muito complicada de ser criada e desenvolvida. Não é mesmo? Bem, tudo depende do que e do como você quer fazer as coisas.

O programa que irei de fato mostrar como sendo o nosso servidor. É tão simples que você poderia colocar ele em um RASPBERRY e usar o raspberry como um mini servidor. E assim poderia permitir um número muito grande de participantes sem precisar realmente compilar novamente o código. Já que o nosso servidor será do tipo dinâmico. O fato de eu dizer que o servidor será dinâmico, indica que o limite de conexões será dado pelas configurações do sistema operacional ou das capacidades do HARDWARE envolvidos. O limite não será definido no código do servidor. Mas antes de nos preocupar com o código do servidor. Vamos ver como deverá ser o código da parte do cliente. Este sim será construído dentro do MQL5.


Implementação básica

Esta parte da implementação será bastante interessante. Ainda mais no que rege o MQL5. Já que vamos usar uma forma bastante peculiar de fazer as coisas. Primeiro, precisamos criar uma janela para interagir com o mini chat. Pois bem, a forma mais simples de fazer isto no MQL5 para ser usado no MetaTrader 5, é usando um indicador. Mas existe um detalhe: Indicadores não tem permissão de fazer uso de soquetes. Já que dependendo da forma como o soquete for programado, pode travar o fluxo de cálculo dos indicadores. Isto por que todos os indicadores fazem parte de um mesmo espaço. Mas então como podemos criar a tal janela? O código mais simples seria justamente este mostrado abaixo:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property indicator_separate_window
04. #property indicator_plots 0
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.    return INIT_SUCCEEDED;
09. }
10. //+------------------------------------------------------------------+
11. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
12. {
13.    return rates_total;
14. }
15. //+------------------------------------------------------------------+

Código inicial do Indicador

Este simples código, consegue criar uma janela para nós. Isto por conta da linha três, que indica que ao colocar este código no gráfico, uma nova janela deverá ser criada. Mas, temos um pequeno problema. O problema é que se deixarmos as coisas assim, poderemos colocar mais de um chat no gráfico. Além deste problema existe um outro, que é o fato de que o indicador não pode utilizar soquetes.

Sendo assim, precisamos modificar este mesmo código, para uma coisa um pouco diferente. Isto para que ele possa ser usado por algum código em que seja possível fazer uso de soquetes. Para simplificar ao máximo, vamos colocar a coisa toda em um Expert Advisor. Lembrando que isto é apenas para demonstração. Você pode fazer a mesma coisa usando um script por exemplo. Mas já que o script é removido do gráfico quando mudamos o tempo do gráfico. Vou mostra como fazer no Expert Advisor. Então a primeira coisa a ser feita é criar o seguinte código, mostrado logo abaixo:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property version   "1.00"
04. //+------------------------------------------------------------------+
05. #define def_IndicatorMiniChat   "Indicators\\Mini Chat\\Mini Chat.ex5"
06. #resource "\\" + def_IndicatorMiniChat
07. //+------------------------------------------------------------------+
08. long gl_id;
09. int subWin;
10. //+------------------------------------------------------------------+
11. int OnInit()
12. {
13.    gl_id = ChartID();
14.    subWin = (int) ChartGetInteger(gl_id, CHART_WINDOWS_TOTAL);
15.    
16.    ChartIndicatorAdd(gl_id, subWin, iCustom(NULL, 0, "::" + def_IndicatorMiniChat));
17. 
18.    return INIT_SUCCEEDED;
19. }
20. //+------------------------------------------------------------------+
21. void OnDeinit(const int reason)
22. {
23.    ChartIndicatorDelete(gl_id, subWin, ChartIndicatorName(gl_id, subWin, 0));
24. }
25. //+------------------------------------------------------------------+
26. void OnTick()
27. {
28. }
29. //+------------------------------------------------------------------+
30. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
31. {
32. }
33. //+------------------------------------------------------------------+

Código inicial do Expert Advisor

Este é o esqueleto básico do nosso Expert Advisor, isto para que uma janela seja aberta. Porém você pode observar o seguinte: Na linha cinco estou definindo que na linha seis usaremos o indicador como um recurso interno do Expert Advisor. Por que estou fazendo isto? O motivo é que quero que o indicador crie uma janela para nos. Isto não pode ser feito pelo Expert Advisor. Então usamos o indicador para isto.

Agora preste atenção: Quando o Expert Advisor, for colocado no gráfico, ele adicionará na linha 16, o nosso indicador, a fim de criar a janela que precisamos. Mas o código do indicador mostrado acima, não nos serve para isto. O motivo é que o indicador mostrado acima, pode ser adicionado no gráfico. E não queremos isto. Queremos que o Expert Advisor adicione o indicador no gráfico para nos. E que isto não seja possível ser feito pelo usuário.

Então como podemos resolver este problema? Simples. Vamos modificar o indicador a fim que o usuário não possar colocar ele no gráfico. Se bem, que uma vez que o Expert Advisor, tenha sido compilado, você pode apagar o executável do indicador. Isto por que, o indicador estará embutido no Expert Advisor. Mas mesmo assim, vamos ver como podemos resolver o problema, caso o executável do indicador esteja acessível ao usuário. Isto para que ele não possa colocar o mesmo no gráfico. A solução pode ser vista no código abaixo:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Base indicator for Mini Chat."
04. #property description "It cannot be used without outside assistance."
05. #property version   "1.00"
06. #property indicator_chart_window
07. #property indicator_plots 0
08. //+------------------------------------------------------------------+
09. #define def_ShortName "Mini Chat"
10. //+------------------------------------------------------------------+
11. int OnInit()
12. {
13.    long id = ChartID();
14.    string sz0 = def_ShortName + "_TMP";
15.    int i0;
16.    
17.    IndicatorSetString(INDICATOR_SHORTNAME, sz0);
18.    for (int c0 = (int)ChartGetInteger(id, CHART_WINDOWS_TOTAL) - 1; c0 >= 0; c0--)
19.       if (ChartIndicatorName(id, c0, 0) == def_ShortName)
20.       {
21.          ChartIndicatorDelete(id, ChartWindowFind(id, sz0), sz0);
22.          return INIT_FAILED;
23.       }
24.    IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);   
25.    i0 = ChartWindowFind(id, def_ShortName);
26.    if ((i0 == 0) || (ChartIndicatorsTotal(id, i0) > 1))
27.    {
28.       ChartIndicatorDelete(id, i0, def_ShortName);
29.       return INIT_FAILED;
30.    }
31.    
32.    return INIT_SUCCEEDED;
33. }
34. //+------------------------------------------------------------------+
35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
36. {
37.    return rates_total;
38. }
39. //+------------------------------------------------------------------+

Primeira modificação no código do Indicador

Este simples código, consegue evitar que o usuário coloque o indicador no gráfico. Isto força e garante que o indicador será colocado apenas e somente pelo Expert Advisor. Já que ele estará embutido no Expert Advisor. Mas vamos entender por que este código evita do usuário colocar o indicador no gráfico. Observe que na linha seis, estou dizendo que o indicador não criará uma nova janela.

Na linha nove, definimos um nome para o indicador. Este nome será importante para nós durante os testes. Agora preste atenção a uma coisa: Para que possamos remover o indicador da lista, precisamos que ele tenha um nome. Este nome NÃO DEVERÁ SER O NOME FINAL. Devemos usar um nome temporário. Isto é feito na linha 14. Logo depois definimos este nome temporário como nome do indicador. Isto é feito na linha 17.

Agora vem a parte interessante. No laço presente na linha 18, faremos uma verificação procurando o nome do indicador na lista de indicadores no gráfico. Porém o nome que estaremos procurando, é o nome visto na linha nove. O teste que faz isto está na linha 19. Quando ele indicar que encontramos o indicador, faremos a execução da linha 21. Esta removerá não o indicador já presente no gráfico. E sim este que estamos tentando colocar. Imediatamente depois de fazer isto, na linha 22 retornamos que a inicialização falhou.

Porém, caso o indicador possa ser colocado, a linha 24, fará com que o nome indicado na linha nove, seja utilizado. Assim garantimos que apenas o indicador embutido no Expert Advisor possa ser usado.

Só que tem um pequeno detalhe ainda faltando. Este é resolvido nas próximas linhas. Já que os trechos explicados antes, evitam do usuário colocar o indicador, depois que ele já está no gráfico. Porém, ainda assim o usuário conseguiria colocar o indicador no gráfico, caso ele não estivesse presente. Para evitar isto, fazemos um novo teste.

Mas primeiro, precisamos saber onde o indicador está sendo colocado. Isto é conseguido na linha 25. Agora preste atenção a uma coisa. Se o indicador está sendo colocado na janela principal, este valor na linha 25 será zero. Caso contrário ele dirá em que sub janela o indicador está sendo colocado. Então no teste feito na linha 26, analisamos isto. Se o valor for zero, teremos que remover o indicador. Mas também faremos a remoção, caso a janela indicada tenha mais de um indicador presente.

Agora talvez você esteja confuso. Se o Expert Advisor vai colocar o indicador no gráfico. Ele não fará isto usando a janela principal? Isto por conta da linha seis, está dizendo que o indicador será colocado na janela principal? Na verdade, quando o Expert Advisor, for colocar o indicador no gráfico, ele fará isto criando uma nova janela no gráfico. Por isto que se o valor retornado na linha 25 for zero, podemos remover o indicador sem problemas. Mas também podemos remover se na mesma janela retornada existir mais alguma outra coisa. Pois isto estará indicando que a tentativa de colocar o indicador está sendo feita pelo usuário.

Desta forma conseguimos limitar as coisas. E impedimos de o usuário conseguir colocar o indicador no gráfico. Muito bem, uma vez que conseguimos fazer isto, podemos partir para a próxima etapa.


Implementando os objetos de interação

Por conta de alguns fatores e para não complicar desnecessariamente o nosso aplicativo em MQL5. Vamos fazer o seguinte: Os objetos de interação serão colocados no Indicador. Já a parte da conexão será colocada no Expert Advisor. Mas faremos as coisas de maneira que se você desejar, poderá melhorar ainda mais este mini chat. Assim vamos dividir todo o trabalho em arquivos de cabeçalho. Como tudo que estamos fazendo está envolvido com o projeto de replay/simulador. Vamos fazer com que este mini chat também possa funcionar dentro deste mesmo modelo. Assim, a primeira coisa a ser feita, será adicionar dois novos eventos na nossa enumeração de eventos. O código pode ser visto 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.          evTicTac,                     //Event of tic-tac
31.          evHideMouse,                  //Hide mouse price line
32.          evShowMouse,                  //Show mouse price line
33.          evHideBarTime,                //Hide bar time
34.          evShowBarTime,                //Show bar time
35.          evHideDailyVar,               //Hide daily variation
36.          evShowDailyVar,               //Show daily variation
37.          evHidePriceVar,               //Hide instantaneous variation
38.          evShowPriceVar,               //Show instantaneous variation
39.          evCtrlReplayInit,             //Initialize replay control
40.          evChartTradeBuy,              //Market buy event
41.          evChartTradeSell,             //Market sales event 
42.          evChartTradeCloseAll,         //Event to close positions
43.          evChartTrade_At_EA,           //Event to communication
44.          evEA_At_ChartTrade,           //Event to communication
45.          evChatWriteSocket,            //Event to Mini Chat
46.          evChatReadSocket              //Event To Mini Chat
47.                   };
48. //+------------------------------------------------------------------+

Código do arquivo Defines.mqh

Observe que o antigo arquivo Defines.mqh, recebeu duas novas linhas. A linha 45 e a linha 46. Estas linhas permitirão que nos comuniquemos via soquetes. Mas estes eventos, somente foram adicionados, por conta do fato de que estaremos separando o mini chat do código interno do Expert Advisor. Ou seja, precisamos de alguma forma, fazer com que o indicador, possa apresentar as mensagens que foram recebidas via soquete. E como o indicador é proibido de utilizar soquetes. Faremos com que o Expert Advisor, observe o soquete e assim que uma mensagem estiver disponível ele a envie para o mini chat. Isto para que possamos ter acesso a ela.

Ok. Agora vamos ver o arquivo de cabeçalho, responsável por criar os objetos no gráfico. Este pode ser visto logo abaixo:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include  "..\Defines.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ShortName    "Mini Chat"
007. #define def_MaxRows      256
008. #define def_FontName     "Lucida Console"
009. #define def_FontSize     12
010. #define def_SizeControls (m_txtHeight + 6)
011. #define macroColorRGBA(A) ((uint)((0xFF << 24) | (A & 0x00FF00) | ((A & 0xFF0000) >> 16) | ((A & 0x0000FF) << 16)))
012. //+------------------------------------------------------------------+
013. class C_Chat
014. {
015.    private   :
016.       long   m_id;
017.       int    m_sub;
018.       int    m_txtHeight;
019.       bool   m_full;
020.       ushort m_Width,
021.              m_Height,
022.              m_index;
023.       uint   m_Pixel[];
024.       string m_ObjEdit,
025.              m_ObjBtn,
026.              m_ObjPanel;
027.       struct st0
028.       {
029.          string info;
030.          bool   loc;
031.       }m_Msgs[def_MaxRows + 1];
032. //+------------------------------------------------------------------+
033.       void Add(string szMsg, bool isloc = false)
034.       {
035.          m_Msgs[m_index].info = szMsg;
036.          m_Msgs[m_index].loc = isloc;
037.          if ((++m_index) > def_MaxRows)
038.          {
039.             m_full = true;
040.             m_index = 0;
041.          }
042.          Paint();
043.       };
044. //+------------------------------------------------------------------+
045.       void Paint(void)
046.       {
047.          int max, count, p0, p1;
048.          
049.          ArrayInitialize(m_Pixel, macroColorRGBA(clrBlack));
050.          if ((p0 = m_Height - def_SizeControls) < 0) return;
051.          max = (int)(floor(p0 / (m_txtHeight * 1.0)));
052.          p1 = m_index - max;
053.          if (m_full)
054.             count = (max > def_MaxRows ? m_index + 1 : (p1 > 0 ? p1 : (def_MaxRows + p1 + 1)));
055.          else
056.             count = (p1 > 0 ? p1 : 0);         
057.          for (ushort row = 0; row < p0; count++)
058.          {
059.             count = (count > def_MaxRows ? 0 : count);
060.             if (count == m_index) break;
061.             TextOut(m_Msgs[count].info, 2, row, 0, m_Pixel, m_Width, m_Height, macroColorRGBA(m_Msgs[count].loc ? clrSkyBlue : clrLime), COLOR_FORMAT_ARGB_NORMALIZE);
062.             row += (ushort) m_txtHeight;
063.          }
064.          ResourceCreate("::" + m_ObjPanel, m_Pixel, m_Width, m_Height, 0, 0, 0, COLOR_FORMAT_ARGB_NORMALIZE);
065.          ChartRedraw();
066.       }
067. //+------------------------------------------------------------------+
068.       void CreateObjEdit(const string szArg)
069.       {
070.          ObjectCreate(m_id, szArg, OBJ_EDIT, m_sub, 0, 0);
071.          ObjectSetInteger(m_id, szArg, OBJPROP_XDISTANCE, 2);
072.          ObjectSetInteger(m_id, szArg, OBJPROP_YDISTANCE, 0);
073.          ObjectSetInteger(m_id, szArg, OBJPROP_YSIZE, def_SizeControls);
074.          ObjectSetString(m_id, szArg, OBJPROP_FONT, def_FontName);
075.          ObjectSetInteger(m_id, szArg, OBJPROP_FONTSIZE, def_FontSize);
076.          ObjectSetInteger(m_id, szArg, OBJPROP_BGCOLOR, clrDarkGray);
077.          ObjectSetInteger(m_id, szArg, OBJPROP_COLOR, clrBlack);
078.          ObjectSetInteger(m_id, szArg, OBJPROP_BORDER_COLOR, clrNavy);
079.       }
080. //+------------------------------------------------------------------+
081.       void CreateObjButton(const string szArg, const string szTxt)
082.       {
083.          ObjectCreate(m_id, szArg, OBJ_BUTTON, m_sub, 0, 0);
084.          ObjectSetInteger(m_id, szArg, OBJPROP_YDISTANCE, 0);
085.          ObjectSetInteger(m_id, szArg, OBJPROP_XSIZE, 70);
086.          ObjectSetInteger(m_id, szArg, OBJPROP_YSIZE, def_SizeControls);
087.          ObjectSetString(m_id, szArg, OBJPROP_FONT, def_FontName);
088.          ObjectSetInteger(m_id, szArg, OBJPROP_FONTSIZE, def_FontSize);
089.          ObjectSetInteger(m_id, szArg, OBJPROP_BGCOLOR, clrSkyBlue);
090.          ObjectSetInteger(m_id, szArg, OBJPROP_COLOR, clrBlack);
091.          ObjectSetInteger(m_id, szArg, OBJPROP_BORDER_COLOR, clrBlack);
092.          ObjectSetString(m_id, szArg, OBJPROP_TEXT, szTxt);
093.       }
094. //+------------------------------------------------------------------+
095.       void CreateObjPanel(const string szArg)
096.       {      
097.          ObjectCreate(m_id, szArg, OBJ_BITMAP_LABEL, m_sub, 0, 0);
098.          ObjectSetInteger(m_id, szArg, OBJPROP_XDISTANCE, 2);
099.          ObjectSetInteger(m_id, szArg, OBJPROP_YDISTANCE, m_txtHeight + 8);
100.          ObjectSetString(m_id, szArg, OBJPROP_BMPFILE, "::" + m_ObjPanel);
101.       }
102. //+------------------------------------------------------------------+
103.    public   :
104. //+------------------------------------------------------------------+
105.       C_Chat()
106.          :m_index(0),
107.           m_full(false),
108.           m_Width(0),
109.           m_Height(0)
110.       {         
111.          int tmp;
112.          
113.          m_sub = ChartWindowFind(m_id = ChartID(), def_ShortName);
114.          TextSetFont(def_FontName, -10 * def_FontSize, 0, 0);
115.          TextGetSize("M", tmp, m_txtHeight);
116.          CreateObjEdit(m_ObjEdit = def_ShortName + " Edit" + (string)ObjectsTotal(m_id));
117.          CreateObjButton(m_ObjBtn = def_ShortName + " Button" + (string)ObjectsTotal(m_id), "Send");
118.          CreateObjPanel(m_ObjPanel = def_ShortName + " Panel" + (string)ObjectsTotal(m_id));
119.       }
120. //+------------------------------------------------------------------+
121.       ~C_Chat()
122.       {
123.          ObjectsDeleteAll(m_id, def_ShortName);
124.       };
125. //+------------------------------------------------------------------+
126.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
127.       {
128.          switch(id)
129.          {
130.             case CHARTEVENT_CHART_CHANGE:
131.                m_Width = (ushort)ChartGetInteger(m_id, CHART_WIDTH_IN_PIXELS, m_sub);
132.                m_Height = (ushort)ChartGetInteger(m_id, CHART_HEIGHT_IN_PIXELS, m_sub);
133.                ObjectSetInteger(m_id, m_ObjEdit, OBJPROP_XSIZE, m_Width - 75);
134.                ObjectSetInteger(m_id, m_ObjBtn, OBJPROP_XDISTANCE, m_Width - 72);
135.                ObjectSetInteger(m_id, m_ObjPanel, OBJPROP_XSIZE, m_Width - 4);
136.                ObjectSetInteger(m_id, m_ObjPanel, OBJPROP_YSIZE, m_Height - 4);
137.                ArrayResize(m_Pixel, m_Width * m_Height);
138.                Paint();
139.                break;
140.             case CHARTEVENT_OBJECT_CLICK:
141.                if (sparam == m_ObjBtn)
142.                {
143.                   string sz0 = ObjectGetString(m_id, m_ObjEdit, OBJPROP_TEXT);
144.                   if (sz0 != "")
145.                   {
146.                      EventChartCustom(m_id, evChatWriteSocket, 0, 0, sz0);
147.                      Add(sz0, true);
148.                      ObjectSetString(m_id, m_ObjEdit, OBJPROP_TEXT, "");
149.                      ObjectSetInteger(m_id, m_ObjBtn, OBJPROP_STATE, 0);
150.                   }                  
151.                }
152.                break;
153.             case CHARTEVENT_CUSTOM + evChatReadSocket:
154.                Add(sparam);
155.                break;
156.             }
157.       }
158. //+------------------------------------------------------------------+
159. };
160. //+------------------------------------------------------------------+
161. #undef macroColorRGBA
162. #undef def_MaxRows
163. //+------------------------------------------------------------------+

Código do arquivo C_Chat.mqh

Este código acima gera os objetos que iremos interagir. Porém, como sei que existem muitos entusiastas lendo estes artigos. Gostaria de pedir calma para aqueles que já tem um bom domínio de programação. Pois farei uma explicação, o mais detalhada possível, deste código. Isto para que aqueles que decidirem fazer uso do mesmo, melhorando assim como ele funciona. Consigam fazer isto. Além é claro, motivar mais pessoas a procurarem desenvolver suas próprias soluções.

Muito bem, então vamos as explicações. Na linha quatro, adicionamos o arquivo de cabeçalho que contém algumas das nossas definições. Apesar de tudo, as definições que realmente usaremos foram as que adicionamos a pouco. Assim, na linha seis, definimos um nome para o nosso indicador. Na linha sete, definimos o número máximo de linhas que o nosso mini chat irá de fato apresentar. Mais a frente você entenderá melhor este valor definido aqui. Já nas linhas oito e nove, definimos o nome e o tamanho da fonte a ser usada. Isto para que seja mais simples ajustar tais coisas, já que desta forma uma vez ajustada as dimensões aqui, todo o mini chat irá se adequar a elas. A linha dez, nos diz qual a altura dos nossos controles, isto para evitar problemas na apresentação dos mesmos. Já a linha onze é apenas uma macro para podermos colocar o texto no gráfico. Depois você entenderá como isto será feito.

Muito bem, feita estas definições, podemos começar a nossa classe. Na linha 15 declaramos a cláusula privativa, assim todas as variáveis e funções presentes depois desta cláusula serão exclusivas desta classe C_Chat. As variáveis definidas da linha 16 a 26 são o mínimo que precisamos. Porém, caso você deseje adicionar mais coisas ao mini chat, será preciso considerar aumentar o número de objetos. Sendo assim, faça isto neste local. Já a estrutura declarada na linha 27 serve de memória para nós. Isto para que possamos ter um acesso rápido, as mensagens postadas por outros usuários.

Um detalhe: O servidor não irá de maneira alguma armazenar as mensagens. Então mensagens postadas antes de você se conectar, estarão perdidas. Existe um outro detalhe também, quando se modifica o tempo gráfico, tudo que estiver no gráfico será removido. Sendo assim as mensagens presentes nesta estrutura também serão perdidas. Se você deseja reaver tais mensagens, deverá pensar em uma forma de salvar e recuperar tais mensagens. Isto em arquivo, para não ter o desprazer de perder as mensagens anteriores. Mas tudo que você precisará fazer, será armazenar esta estrutura definida na linha 27. E quando for ler o arquivo para restaurar esta mesma estrutura, você deverá chamar o procedimento que explicarei abaixo. Simples assim.

Agora observe o a linha 31. Nele fazemos uso da declaração feita na linha sete. Mas observe que estamos somando um. Isto se deve ao fato de que o MQL5, assim como o C/C++, começamos contando a partir do zero. Assim adicionando um, dizemos que realmente queremos que as últimas 256 linhas sejam preservadas. Conforme declarado na linha sete. Você pode colocar mais ou menos linhas. Fica ao seu critério.

Agora começa a parte interessante desta classe. Ou seja, os procedimentos e funções que permitem o mini chat funcionar. O primeiro procedimento que temos é o presente na linha 33. Este tem como objetivo, adicionar novas mensagens na estrutura declarada na linha 27. Aqui, simplesmente atribuímos os valores aos seus correspondentes. Isto é feito nas linhas 35 e 36.

Agora preste atenção. Na linha 37, testamos se o nosso contador atingiu o limite de mensagens. Quando isto ocorrer, precisamos fazer com que o contador volte a posição zero. Isto é feito na linha 40. E para que saibamos que este limite foi atingido, na linha 39, atribuímos o valor verdadeiro para a variável que está indicando uma lista cheia de mensagens.

Mas por que estamos fazendo isto? O motivo, é que quando o número máximo de mensagens for alcançado, começaremos a sobrescrever as mensagens mais antigas. Isto de fato funciona como uma lista circular, onde uma vez que a lista esteja cheia. Iremos começar a gravar as novas mensagens nas posições antigas. Mas sempre mantendo a lista cheia. Você pode modificar isto se desejar. Isto também fica ao seu critério.

O grande detalhe aqui, fica por conta do fato de que uma vez a mensagem sendo adicionada, precisamos colocá-la no gráfico. Isto é feito no próximo procedimento, que pode ser visto na linha 45.

Mas antes de vermos este procedimento. Vamos ver algumas outras coisas. Isto para que o procedimento da linha 45, seja corretamente entendido. Com isto pulamos para a linha 95. Nela criamos um painel que conterá o texto presente na lista circular. Fazemos isto de uma forma o mais simples possível. Ou seja, na linha 97 criamos o objeto OBJ_BITMAP_LABEL. Já nas linhas 98 e 99, dizemos onde está o canto superior do objeto. Mas para nós, o que realmente é importante, é a linha 100. Pois é nesta linha que dizemos qual será o bitmap a ser usado.

Mas espere um pouco, ai. Como assim, bitmap? Nosso mini chat não irá apenas apresentar mensagens em formas de texto? Por que usar um bitmap. Não faz sentido. De fato, iremos realmente apresentar mensagens em forma de texto. Porém, o MQL5, não conta com uma forma simples de colocarmos texto diretamente no gráfico. Isto da forma como precisamos. Fazer uso da função de biblioteca Comment não é suficiente ou adequado para nós. Precisamos realmente controlar onde e como o texto será apresentado. Assim a forma correta de fazer isto, é usando um bitmap. Ou seja, o MetaTrader 5, irá de fato desenhar as letras em um bitmap e este será apresentado para a nossa leitura no gráfico.

Isto pode soar complicado a primeira vista. Mas não é assim tão complicado. Na verdade é algo até bastante simples de ser feito. Tudo que você precisa entender é que nesta linha 100 é onde definimos qual será o nome do bitmap a ser usado. Agora podemos voltar na linha 45. Pois o que será feito lá. Agora passará a fazer sentido.

Para começar, na linha 49, limpamos a matriz de pixel. A cor a ser usada pode ser modificada, por outra qualquer, no caso o fundo será da cor clrBlack, ou seja, o fundo do painel de texto será preto. Mas você pode mudar isto. Logo depois de ter feito isto, efetuamos um pequeno cálculo, este tem como objetivo verificar se a janela do mini chat não precisa conter o painel de texto. Se este for o caso, encerramos por aqui. Mas se algum texto puder ser apresentado, desenharemos o mesmo.

Então na linha 51, calculamos a quantidade máxima de linhas possíveis dentro da região permitida o painel de texto. Depois disto na linha 52 procuramos o ponto inicial da lista circular, isto para que todas as linhas possíveis sejam apresentadas. Agora existe um detalhe. Quando a lista estiver cheia, o valor da variável m_index, pode ser menor, maior ou igual, a quantidade de linhas possíveis no painel. Então precisamos reajustar adequadamente este mesmo valor.

Assim temos os ajustes que são feitos entre as linhas 53 e 56. Agora sim, a variável que será utilizada para contagem, está de fato apontando para a linha mais antiga a ser apresentada. Então entramos no laço da linha 57. Este laço talvez possa lhe parecer estranho, mas ele é bastante simples. Tudo que ele faz é varrer a lista circular, posição por posição e na linha 61 usando uma chamada da biblioteca do MQL5, desenhar o texto no bitmap.

Assim que este laço terminar o bitmap conterá o texto desenhado nele. Então precisamos dizer ao MetaTrader 5, que o bitmap já pode ser utilizado. Isto é feito na linha 64. Prestem bastante atenção a esta sequência de eventos. Primeiro definimos o nome do bitmap. Depois desenhamos ele. E finalmente dizemos ao MetaTrader 5, que o bitmap já pode ser apresentado no gráfico. E é neste ponto da linha 64 que isto acontece. Simples não é mesmo?

Já os procedimentos da linha 68 até a linha 93 são praticamente comuns no que diz respeito a programação no MQL5. Dispensando assim maiores comentários. Porém, a explicação ainda não terminou. Vamos agora para o contructor da classe, este já se encontra depois da declaração de uma cláusula publica presente na linha 103. Então tudo que estiver depois da linha 103, será visível fora da classe. Neste constructor, presente na linha 105, inicializaremos o nosso mini chat. Isto é feito em etapas bastante simples.

Primeiro, precisamos capturar o índex da janela onde o mini chat será apresentado. Isto é feito na linha 113. Logo depois na linha 114, dizemos ao MetaTrader 5 como será definida a fonte a ser usada para a criação do texto em formato bitmap. Na linha 115, capturamos a altura das letras. Isto por que dependendo da configuração do sistema operacional, as letras podem ter um tamanho diferente. Então para evitar uma apresentação estranha, capturamos a altura do texto.

Logo depois entre as linhas 116 a 118 criamos os objetos que precisamos. Se você precisar de mais objetos, você deverá adicioná-los neste ponto. Mas tome o cuidado de seguir as regras de criação, que está sendo mostrada nos objetos visto no código.

O destructor presente na linha 121, tem como única tarefa, remover os objetos criados do gráfico. Por isto temos apenas a linha 123.

Finalmente chegamos a linha 126, onde tratamos das mensagens que o MetaTrader 5 direcionará ao nosso indicador. Lembre-se ainda estamos dentro do código do indicador. Para ser o mais simples possível, aqui estamos tratando apenas de três tipos de eventos, ou mensagens. Uma que é a CHARTEVENT_CHART_CHANGE, que definirá a correta posição e dimensão dos objetos. Sendo a primeira mensagem a ser gerada pelo MetaTrader 5, assim que o nosso código for colocado no gráfico.

Também tratamos da mensagem CHARTEVENT_OBJECT_CLICK, que ocorrerá quando um clique ocorrer no gráfico. Neste caso na linha 141, testamos se o clique foi no botão de enviar a mensagem. Se este for o caso, na linha 143 capturamos o texto que está na área de edição. Isto para poder enviar ele logo depois. Mas na linha 144, averiguamos se o texto não está vazio. Se não estiver, na linha 146 disparamos um evento customizado. A fim de enviar este mesmo texto para o Expert Advisor.

Um detalhe importante: Apesar do Expert Advisor poder acessar diretamente o texto, no objeto de edição. Não devemos correr o risco de deixar que o Expert Advisor busque o texto. Isto por que ele já poderá ter sido destruído pelo indicador. Isto por conta da linha 148. De qualquer forma o mesmo texto que enviaremos via evento customizado, será lançado no painel na linha 147.

E para terminar, na linha 153, temos um evento customizado. Este visa capturar o texto que o Expert Advisor recebeu do soquete e colocar ele no painel de texto do nosso mini chat. Talvez neste momento, isto possa parecer confuso para você caro leitor. Ainda mais se você caiu aqui de paraquedas direto neste artigo. Em artigos anteriores, nesta mesma sequência expliquei em detalhes como trabalhar com eventos customizados. Caso tenha curiosidade, dê uma lida nos artigos anteriores. Eles ajudaram bastante o seu entendimento do que está acontecendo aqui.

Muito bem, então como último código a ser visto neste artigo. Vamos ver como ficou o código final do Indicador Mini Chat. Este pode ser visto na íntegra logo abaixo:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Base indicator for Mini Chat."
04. #property description "It cannot be used without outside assistance."
05. #property version   "1.00"
06. #property link "https://www.mql5.com/pt/articles/12672"
07. #property indicator_chart_window
08. #property indicator_plots 0
09. //+------------------------------------------------------------------+
10. #include <Market Replay\Mini Chat\C_Chat.mqh>
11. //+------------------------------------------------------------------+
12. C_Chat *Chat;
13. //+------------------------------------------------------------------+
14. int OnInit()
15. {
16.    long id = ChartID();
17.    string sz0 = def_ShortName + "_TMP";
18.    int i0;
19.    
20.    IndicatorSetString(INDICATOR_SHORTNAME, sz0);
21.    for (int c0 = (int)ChartGetInteger(id, CHART_WINDOWS_TOTAL) - 1; c0 >= 0; c0--)
22.       if (ChartIndicatorName(id, c0, 0) == def_ShortName)
23.       {
24.          ChartIndicatorDelete(id, ChartWindowFind(id, sz0), sz0);
25.          return INIT_FAILED;
26.       }
27.    IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);   
28.    i0 = ChartWindowFind(id, def_ShortName);
29.    if ((i0 == 0) || (ChartIndicatorsTotal(id, i0) > 1))
30.    {
31.       ChartIndicatorDelete(id, i0, def_ShortName);
32.       return INIT_FAILED;
33.    }
34.    
35.    Chat = new C_Chat();
36.    
37.    return INIT_SUCCEEDED;
38. }
39. //+------------------------------------------------------------------+
40. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
41. {
42.    return rates_total;
43. }
44. //+------------------------------------------------------------------+
45. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
46. {
47.    (*Chat).DispatchMessage(id, lparam, dparam, sparam);
48. }
49. //+------------------------------------------------------------------+
50. void OnDeinit(const int reason)
51. {
52.    delete Chat;
53. }
54. //+------------------------------------------------------------------+

Código final do indicador


Considerações finais

Neste artigo apresentei a primeira parte do código do mini chat. Ainda falta falar do código do Expert Advisor e principalmente do código do servidor. Mas como isto é um assunto bastante extenso. Deixarei você, caro leitor, se deliciar com o código visto até aqui. Nos vemos no próximo artigo, onde terminaremos de construir o mini chat com o servidor e ver ele funcionando na prática.

ArquivoDescrição
Experts\Expert Advisor.mq5
Demonstra a interação entre o Chart Trade e o Expert Advisor (É necessário o Mouse Study para interação)
Indicators\Chart Trade.mq5Cria a janela para configuração da ordem a ser enviada (É necessário o Mouse Study para interação)
Indicators\Market Replay.mq5Cria os controles para interação com o serviço de replay/simulador (É necessário o Mouse Study para interação)
Indicators\Mouse Study.mq5Permite interação entre os controles gráficos e o usuário (Necessário tanto para operar o replay simulador, quanto no mercado real)
Services\Market Replay.mq5Cria e mantém o serviço de replay e simulação de mercado (Arquivo principal de todo o sistema)
Code VS C++\Servidor.cppCria e mantém um soquete servidor criado em C++ (Versão Mini Chat)
Code in Python\Server.pyCria e mantém um soquete em python para comunicação entre o MetaTrader 5 e o Excel
Scripts\CheckSocket.mq5Permite efetuar um teste de conexão com um soquete externo
Indicators\Mini Chat.mq5Permite implementar um mini chat via indicador (Necessário uso de um servidor para funcionar)
Experts\Mini Chat.mq5Permite implementar um mini chat via Expert Advisor (Necessário uso de um servidor para funcionar)
Arquivos anexados |
Anexo.zip (560.03 KB)
Redes neurais em trading: Transformer para nuvens de pontos (Pointformer) Redes neurais em trading: Transformer para nuvens de pontos (Pointformer)
Neste artigo, falaremos sobre os algoritmos que utilizam métodos de atenção para resolver tarefas de detecção de objetos em nuvens de pontos. A detecção de objetos em nuvens de pontos é de grande importância para diversas aplicações práticas.
Redes neurais em trading: Aprendizado hierárquico de características em nuvens de pontos Redes neurais em trading: Aprendizado hierárquico de características em nuvens de pontos
Continuamos estudando algoritmos para extração de características de nuvens de pontos. Neste artigo, exploraremos mecanismos para aumentar a eficiência do método PointNet.
Soluções simples para trabalhar com indicadores Soluções simples para trabalhar com indicadores
Neste artigo, explicarei como criar um painel simples para ajustar as configurações de um indicador diretamente no gráfico e quais modificações são necessárias no indicador para integrar esse painel. O artigo é voltado exclusivamente para quem está começando a aprender a linguagem MQL5.
Do básico ao intermediário: Struct (II) Do básico ao intermediário: Struct (II)
Neste artigo iremos entender por que estrutura foram criadas em linguagens de programação como o MQL5. Assim como também por que alguns momentos, estruturas formas ideais de transferir valores entre funções e procedimentos. Enquanto em outros momentos, elas podem não ser a melhor forma de se fazer isto.