preview
Desenvolvendo um sistema de Replay (Parte 44): Projeto do Chart Trade (III)

Desenvolvendo um sistema de Replay (Parte 44): Projeto do Chart Trade (III)

MetaTrader 5Exemplos | 21 março 2024, 17:24
200 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Desenvolvendo um sistema de Replay (Parte 43): Projeto do Chart Trade (II), expliquei como você pode manipular os dados do template a fim de usá-los em um OBJ_CHART. Mas lá apenas introduzi a questão, mas sem entrar em muitos detalhes, já que naquela versão o trabalho foi feito de uma maneira bem simplificada. No entanto, ela foi feita daquela forma, justamente para facilitar a explicação do conteúdo. Pois apesar de parecer simples fazer certas coisas, algumas não são tão evidentes, e sem compreender a parte mais simples e básica, você não irá de fato entender o que estou fazendo.

Então apesar de aquele código funcionar, como deu para se ver. O mesmo não nos permite fazer algumas coisas. Melhor dizendo, fazer algumas coisas, será muito mais complicado, se não for feita algum tipo de melhoria na modelagem dos dados. Tal melhoria passa justamente por uma codificação, um pouco mais elaborada. Mas o conceito usado será o mesmo. Apenas o código será um pouco mais complexo.

Além deste pequeno fato, vamos resolver uma outra questão. Se você notou, e além disto, falei no próprio artigo, aquele código não é muito eficiente, já que temos, ao meu ver, um excesso de chamadas para ajustar as coisas. Então para resolver isto iremos fazer algumas pequenas mudanças no código. Estas mudanças terão como resultado, uma drástica redução, em termos de chamadas, ao mesmo tempo que irá promover uma modelagem mais adequada dos dados.


Nasce uma nova classe C_AdjustTemplate

Para promover as melhorias que precisamos, teremos que criar uma nova classe. Seu código pode ser visto na integra logo abaixo:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "../Auxiliar/C_Terminal.mqh"
05. //+------------------------------------------------------------------+
06. class C_AdjustTemplate
07. {
08.     private :
09.             string m_szName[];
10.             string m_szFind[];
11.             string m_szReplace[];
12.             string m_szFileName;
13.             int m_maxIndex;
14.             int m_FileIn;
15.             int m_FileOut;
16.             bool m_bSimple;
17.     public  :
18. //+------------------------------------------------------------------+
19.             C_AdjustTemplate(const string szFileNameIn, string szFileNameOut = NULL)
20.                     :m_maxIndex(0),
21.                      m_szFileName(szFileNameIn),
22.                      m_FileIn(INVALID_HANDLE),
23.                      m_FileOut(INVALID_HANDLE)
24.                     {
25.                             ResetLastError();
26.                             if ((m_FileIn = FileOpen(szFileNameIn, FILE_TXT | FILE_READ)) == INVALID_HANDLESetUserError(C_Terminal::ERR_FileAcess);
27.                             if (_LastError == ERR_SUCCESS)
28.                             {
29.                                     if (!(m_bSimple = (StringLen(szFileNameOut) > 0))) szFileNameOut = szFileNameIn + "_T";                                                 
30.                                     if ((m_FileOut = FileOpen(szFileNameOut, FILE_TXT | FILE_WRITE)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess);
31.                             }
32.                     }
33. //+------------------------------------------------------------------+
34.             ~C_AdjustTemplate()
35.                     {
36.                             FileClose(m_FileIn);
37.                             if (m_FileOut != INVALID_HANDLE)
38.                             {
39.                                     FileClose(m_FileOut);
40.                                     if ((!m_bSimple) && (_LastError == ERR_SUCCESS)) FileMove(m_szFileName + "_T", 0, m_szFileName, FILE_REWRITE);
41.                                     if ((!m_bSimple) && (_LastError != ERR_SUCCESS)) FileDelete(m_szFileName + "_T");
42.                             }
43.                             ArrayResize(m_szName, 0);
44.                             ArrayResize(m_szFind, 0);
45.                             ArrayResize(m_szReplace, 0);
46.                     }
47. //+------------------------------------------------------------------+
48.             void Add(const string szName, const string szFind, const string szReplace)
49.                     {
50.                             m_maxIndex++;
51.                             ArrayResize(m_szName, m_maxIndex);
52.                             ArrayResize(m_szFind, m_maxIndex);
53.                             ArrayResize(m_szReplace, m_maxIndex);
54.                             m_szName[m_maxIndex - 1] = szName;
55.                             m_szFind[m_maxIndex - 1] = szFind;
56.                             m_szReplace[m_maxIndex - 1] = szReplace;
57.                     }
58. //+------------------------------------------------------------------+
59.             string Get(const string szName, const string szFind)
60.                     {
61.                             for (int c0 = 0; c0 < m_maxIndex; c0++) if ((m_szName[c0] == szName) && (m_szFind[c0] == szFind)) return m_szReplace[c0];
62.                             
63.                             return NULL;
64.                     }
65. //+------------------------------------------------------------------+
66.             void Execute(void)
67.                     {
68.                             string sz0, tmp, res[];
69.                             int count0 = 0, i0;
70.                             
71.                             if ((m_FileIn != INVALID_HANDLE) && (m_FileOut != INVALID_HANDLE)) while ((!FileIsEnding(m_FileIn)) && (_LastError == ERR_SUCCESS))
72.                             {
73.                                     sz0 = FileReadString(m_FileIn);
74.                                     if (sz0 == "<object>") count0 = 1;
75.                                     if (sz0 == "</object>") count0 = 0;
76.                                     if (count0 > 0) if (StringSplit(sz0, '=', res) > 1)
77.                                     {
78.                                             i0 = (count0 == 1 ? 0 : i0);
79.                                             for (int c0 = 0; (c0 < m_maxIndex) && (count0 == 1); i0 = c0, c0++) count0 = (res[1] == (tmp = m_szName[c0]) ? 2 : count0);
80.                                             for (int c0 = i0; (c0 < m_maxIndex) && (count0 == 2); c0++) if ((res[0] == m_szFind[c0]) && (tmp == m_szName[c0]))
81.                                             {
82.                                                     if (StringLen(m_szReplace[c0])) sz0 =  m_szFind[c0] + "=" + m_szReplace[c0];
83.                                                     else m_szReplace[c0] = res[1];
84.                                             }
85.                                     }
86.                                     FileWriteString(m_FileOut, sz0 + "\r\n");
87.                             };
88.                     }
89. //+------------------------------------------------------------------+
90. };
91. //+------------------------------------------------------------------+

Código fonte: C_AdjustTemplate

Este código contém exatamente o que precisamos. Se você observar este código daqui, e olhar o código da classe C_ChartFloatingRAD do artigo anterior. Irá notar que o conteúdo presente entre as linhas 38 e 90 da classe C_ChartFloatingRAD, está aqui. Mas não com a mesma aparência. Isto se deve ao fato, de que a modelagem dos dados nesta classe C_AdjustTemplate, é feita de maneira a promover uma execução mais eficiente. Você irá notar isto quando mais a frente neste artigo, for mostrado o novo código da classe C_ChartFloatingRAD. Mas isto irá ficar mais para frente. Primeiro vamos entender o que está acontecendo nesta classe C_AdjustTemplate.

Apesar de parecer complexo, e de difícil compreensão. Este código da classe C_AdjustTemplate, é bastante simples. No entanto, ele é voltado para ser executado, como se fosse uma única função, apesar de contar com mais funções. Para você de fato compreender a ideia, esqueça o fato de estar trabalhando com código, com MetaTrader, ou com MQL5. Pense como se estivesse trabalhando com partes de uma máquina, assim será mais simples compreender as coisas. A classe C_AdjustTemplate, deve ser pensada como se fosse um template. Sim, isto mesmo. Pense nela como se fosse um arquivo template. Aquele mesmo arquivo visto no artigo anterior.

Se você pensar assim, vai compreender o que está de fato acontecendo, e por que deveremos trabalhar com esta classe, da maneira como iremos fazer mais a frente. Então quando você usa o constructor da classe, está na verdade abrindo o template, para manipular o que esta dentro dele. Quando você usa o destructor, está dizendo: Pronto, agora MetaTrader, você pode usar o template. As demais funções servem como ferramentas para ajustar o template.

Então com base nisto, vamos entender como cada parte desta classe funciona. Começaremos com o constructor. Ele se inicia na linha 19, nesta podemos ver que, obrigatoriamente, teremos que informar uma string, mas poderemos informa, opcionalmente uma segunda string. Por que fazer assim ?!?! O motivo é simples: Sobrecarga. Se a sobrecarga não fosse possível, teríamos que codificar 2 constructores, mas já que ela é possível, fazemos uso da mesma. Mas esta sobrecarga, não é de fato aquela convencional. Esta é dirigida para ser feita desta forma.

Feito isto, entre as linhas 20 e 23, iniciamos antecipadamente algumas variáveis. Isto é importante para nos, apesar do compilador, fazer uma inicialização implícita, sempre é melhor faze-la de forma explicita. Assim saberemos exatamente qual é o valor de cada variável.

Agora atenção ao seguinte fato: Na linha 25, estamos "zerando" a constante _LastError. Então se existir alguma falha antes do constructor ser chamado, você deverá testar isto, caso contrário, irá perder o valor informado na constante de erro. Já expliquei em artigos passados sobre, o por que de fazer as coisas assim. Leia os mesmos para mais detalhes.

Na linha 26, fazemos a tentativa de abrir o arquivo de origem, este deverá obrigatoriamente ser informado. Se esta tentativa falhar, iremos informar isto na constante _LastError. Se tivermos sucesso em abrir o arquivo, a constante _LastError irá conter o valor ERR_SUCESS, e com isto o teste efetuado na linha 27, irá passar, nos permitindo executar a próxima etapa.

Nesta etapa iremos verificar na linha 29, se está sendo informado um nome para o arquivo destino. Caso este nome não tenha sido informado, iremos então trabalhar com um arquivo temporário. Este terá o nome baseado no nome do arquivo de origem. Tendo um nome para o arquivo de destino. Podemos então executar a linha 30, que irá tentar criar o arquivo de destino. Se esta tentativa falhar, iremos informar isto na constante _LastError. Se tudo ocorrer bem, a constante _LastError irá conter o valor ERR_SUCESS, e teremos os handles dos arquivos. Estes irão ser usados para manipular os arquivos, por conta disto não devemos tentar fazer nada, externamente, nos arquivos, até que os handles sejam fechados. Pense no seguinte: A máquina está aberta, se você ligar ela pode dar problema, ou o famoso B.O.

Muito bem, vamos seguir o código em sua ordem de edição. Desta forma, chegamos a linha 34, onde começa o código do destructor da classe. A primeira coisa que fazemos, e na linha 36, é fechar o arquivo de entrada. Um detalhe, este arquivo somente será fechado se o handle do mesmo for válido. Ou seja, o arquivo deverá se encontrar aberto. Já na linha 37, testamos se o arquivo de saída se encontra aberto. O fato de fazer isto, é justamente para evitar a tentativa de executar as próximas linhas sem necessidade.

Então caso o arquivo de destino esteja aberto, testamos na linha 40, se foi informado um nome para ele, e se não ocorreu nenhum erro durante o processo de ajuste. Caso esteja tudo ok, iremos renomear o arquivo para representar o arquivo esperado pelo restante do indicador. De qualquer maneira, na linha 41, iremos remover o arquivo temporário usado, se algo tiver dado errado.

Entre as linhas 43 e 45, devolvemos a memória alocada. Este tipo de coisa é bastante importante, muitos se esquecem de fazer isto. Mas como uma boa prática de programação, se você alocar memória deverá sempre devolver ela. Assim o MetaTrader 5, não irá consumir recursos de forma excessiva e desnecessária.

Na sequencia, temos um procedimento bem básico e simples, ele se inicia na linha 50, onde incrementamos o contador, para logo em seguida alocar memória, para armazenar os dados que iremos usar depois. Esta alocação é feita entre as linha 51 a 53. Mas observem que nas linhas 54 a 56, a forma como estaremos armazenando as informações. Por tal procedimento ser simples, não iremos entrar em mais detalhes. Mas na linha 59 temos uma função que é curiosa.

Esta função que se inicia na linha 59, apesar de ser muito simples, é bastante curiosa. Não pelo que ela faz, mas sim como ela faz. Observem na linha 60, que de fato é a única linha desta função. Aqui temos um laço onde iremos varrer todas as posições que foram adicionadas durante o procedimento presente na linha 50. A pergunta é: Por que, você como programador, iria querer ler uma informação que você gravou na classe, usando para isto o procedimento da linha 50 ?!?! Isto parece não fazer sentido. De fato, se você olhar apenas e somente o código da classe, não faz sentido. Mas, porém, toda via e entretanto, existe um pequeno detalhe nesta história. e este detalhe começa na linha 66.

Este procedimento Execute, que se inicia na linha 66, é um procedimento extremamente tenso. O motivo disto, é que pode acontecer diversas coisas de errado. Basicamente, podemos ter os seguintes erros:

  • O arquivo de entrada não pode ser lido;
  • O arquivo de saída pode não estar mais acessível;
  • A função StringSplit pode falhar.

Qualquer uma destas coisas, irá fazer com que a constante de erro mude. Caso isto aconteça, o laço while, que está na linha 71, irá finalizar antecipadamente, fazendo com que todo o indicador falhe ao ser lançado no gráfico. Lembre-se do seguinte fato: Se a constante _LastError contiver um valor diferente de ERR_SUCESS, no momento que o destructor for executado, o template não será atualizado. E caso seja a primeira chamada, ele não será criado. Caso ele não exista, o indicador não será lançado no gráfico. Por conta disto, este procedimento Execute é tão tenso.

No entanto, vamos considerar que tudo esteja funcionando perfeitamente. Então vamos ver o que acontece dentro do laço while.

Na linha 73, lemos uma string do arquivo de origem. Esta string será composta de uma linha completa. Fazendo a leitura desta forma, será bem mais simples testar as demais coisas. Então, na linha 74, testamos se estaremos entrando em uma definição de algum objeto. Já na linha 75, testamos se a definição do objeto foi encerrada.

Estes testes são importantes para agilizar o processo de tradução e ajuste do template. Já que se não estamos lidando com algum objeto, podemos simplesmente gravar a informação no arquivo de destino. Esta gravação é feita na linha 86. Prestem atenção a isto. Pois agora iremos ver como o template de origem será manipulado e ajustado a fim de gerar o que desejamos.

Quando estamos dentro de um objeto, temos o teste da linha 76, permitindo que façamos uma "quebra" da string. Esta "quebra", se dará justamente no sinal de igualdade ( = ), o que representa que estamos definindo algum parâmetro, para alguma propriedade do objeto. Pois bem, se isto for verdadeiro, que estamos definindo uma propriedade, estes testes passarão, nos permitindo executar a linha 78. Esta é simplesmente, irá ajusta a memória temporária. Mas a questão de fato se dá nas próximas linhas.

Na linha 79, iremos varrer todos os dados que foram adicionados durante a execução das chamadas Add ( procedimento da linha 48 ). Se por um acaso, encontrarmos o valor do parâmetro, no template, isto indica que estamos lidando com o objeto que procuramos. Com isto armazenamos temporariamente o nome do objeto, e indicamos que iremos fazer um segundo nível de analise. Por conta deste fato, de estamos fazendo um segundo nível, esta linha 79 não será executada novamente, dentro do mesmo objeto. Isto nos força a garantir que o template, deva ter a mesma formatação que é vista no artigo anterior, Desenvolvendo um sistema de Replay (Parte 43): Projeto do Chart Trade (II). Ou seja o arquivo tem que ser exatamente aquele. Se você o mudar, garanta que ele permaneça o mais parecido com aquele.

Pois bem, já que estamos no segundo nível, na linha 80, teremos um outro laço. Importante: O laço da linha 79 e 80, nunca são executados juntos. Será sempre executado, um ou outro, nunca os dois. Salvo a primeira chamada. Este, de que ambas linhas 79 e 80 não serem executadas, parece estranho, mas de fato é o que acontece. Mas caso a linha 80 venha a ser executada, iremos ter dentro do laço, um teste a fim se verificar se encontramos a propriedade desejada do objeto. Observe que o nome do objeto é importante, por conta disto, gravamos ele temporariamente durante o laço da linha 79.

Caso este teste, indique que a propriedade do objeto, foi encontrada. Iremos fazer um segundo teste, este na linha 82. Neste ponto, iremos ter a justificativa da presença da função encontrada na linha 59. Se durante a programação, que será vista logo mais, você disser a classe C_AdjustTemplate, que não sabe qual é o parâmetro a ser usado na propriedade do objeto. Este teste na linha 82 irá fazer com que a linha 83 seja executada, capturando assim o valor presente na propriedade do objeto. Caso você informe o valor a ser usado, este será gravado no template.

Este tipo de coisa é que torna esta classe C_AdjustTemplate, tão interessante. Já que você pode pedir para ela, dizer qual é o valor da propriedade, ou informar qual deverá ser o valor a ser usado.

Desta maneira, terminamos a explicação da classe C_Template. Agora vamos ver como ficou a classe C_ChartFloatingRAD. Pois como você pode imaginar, a mesma foi modificada e passou a ser ainda mais interessante.


Uma nova cara para a classe C_ChartFloatingRAD

Apesar de não vim a mostrar o código final desta classe neste artigo, e o motivo é que quero explicar cada detalhe com calma. Você irá notar, que ela agora, estará bem mais complexa, do que o código visto no artigo anterior. No entanto, mesmo assim, grande parte do código se manteve. Desta maneira, é bom que você acompanhe a sequencia de artigos, caso contrário irá perder alguns detalhes, e estes podem fazer a diferença, no entendimento geral.

Logo abaixo você pode ver o código na integra da classe C_ChartFloatingRAD, até o atual momento.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Mouse.mqh"
005. #include "C_AdjustTemplate.mqh"
006. //+------------------------------------------------------------------+
007. class C_ChartFloatingRAD : private C_Terminal
008. {
009.    private :
010.            enum eObjectsIDE {MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL};
011.            struct st00
012.            {
013.                    int     x, y;
014.                    string  szObj_Chart,
015.                            szFileNameTemplate;
016.                    long    WinHandle;
017.                    double  FinanceTake,
018.                            FinanceStop;
019.                    int     Leverage;
020.                    bool    IsDayTrade,
021.                            IsMaximized;
022.                    struct st01
023.                    {
024.                            int x, y, w, h;
025.                    }Regions[MSG_NULL];
026.            }m_Info;
027. //+------------------------------------------------------------------+
028.            C_Mouse  *m_Mouse;
029. //+------------------------------------------------------------------+
030.            void CreateWindowRAD(int x, int y, int w, int h)
031.                    {
032.                            m_Info.szObj_Chart = (string)ObjectsTotal(GetInfoTerminal().ID);
033.                            ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0);
034.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x = x);
035.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y = y);
036.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w);
037.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h);
038.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false);
039.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false);
040.                            m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID);
041.                    };
042. //+------------------------------------------------------------------+
043. inline void UpdateChartTemplate(void)
044.                    {
045.                            ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
046.                            ChartRedraw(m_Info.WinHandle);
047.                    }
048. //+------------------------------------------------------------------+
049. inline double PointsToFinance(const double Points)
050.                    {                               
051.                            return Points * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade;
052.                    };
053. //+------------------------------------------------------------------+
054. inline void AdjustTemplate(const bool bFirst = false)
055.                    {
056. #define macro_AddAdjust(A) {                         \
057.              (*Template).Add(A, "size_x", NULL);     \
058.              (*Template).Add(A, "size_y", NULL);     \
059.              (*Template).Add(A, "pos_x", NULL);      \
060.              (*Template).Add(A, "pos_y", NULL);      \
061.                            }
062. #define macro_GetAdjust(A) {                                                                          \
063.              m_Info.Regions[A].x = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_x"));  \
064.              m_Info.Regions[A].y = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_y"));  \
065.              m_Info.Regions[A].w = (int) StringToInteger((*Template).Get(EnumToString(A), "size_x")); \
066.              m_Info.Regions[A].h = (int) StringToInteger((*Template).Get(EnumToString(A), "size_y")); \                                               
067.                            }
068.                            
069.                            C_AdjustTemplate *Template;
070.                            
071.                            if (bFirst)
072.                            {
073.                                    Template = new C_AdjustTemplate("Chart Trade/IDE_RAD.tpl", m_Info.szFileNameTemplate = StringFormat("Chart Trade/%u.tpl", GetInfoTerminal().ID));
074.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_AddAdjust(EnumToString(c0));
075.                            }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
076.                            Template.Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
077.                            Template.Add("MSG_LEVERAGE_VALUE", "descr", (string)m_Info.Leverage);
078.                            Template.Add("MSG_TAKE_VALUE", "descr", (string)m_Info.FinanceTake);
079.                            Template.Add("MSG_STOP_VALUE", "descr", (string)m_Info.FinanceStop);
080.                            Template.Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0"));
081.                            Template.Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
082.                            Template.Execute();
083.                            if (bFirst) for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_GetAdjust(c0);
084.                            
085.                            delete Template;
086.                            
087.                            UpdateChartTemplate();
088.                                                            
089. #undef macro_AddAdjust
090. #undef macro_GetAdjust
091.                    }
092. //+------------------------------------------------------------------+
093.            eObjectsIDE CheckMousePosition(const int x, const int y)
094.                    {
095.                            int xi, yi, xf, yf;
096.                            
097.                            for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++)
098.                            {
099.                                    xi = m_Info.x + m_Info.Regions[c0].x;
100.                                    yi = m_Info.y + m_Info.Regions[c0].y;
101.                                    xf = xi + m_Info.Regions[c0].w;
102.                                    yf = yi + m_Info.Regions[c0].h;
103.                                    if ((x > xi) && (y > yi) && (x < xf) && (y < yf) && ((c0 == MSG_MAX_MIN) || (c0 == MSG_TITLE_IDE) || (m_Info.IsMaximized))) return c0;
104.                            }
105.                            return MSG_NULL;
106.                    }
107. //+------------------------------------------------------------------+
108.    public  :
109. //+------------------------------------------------------------------+
110.            C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const int Leverage, const double FinanceTake, const double FinanceStop)
111.                    :C_Terminal()
112.                    {
113.                            m_Mouse = MousePtr;
114.                            m_Info.Leverage = (Leverage < 0 ? 1 : Leverage);
115.                            m_Info.FinanceTake = PointsToFinance(FinanceToPoints(MathAbs(FinanceTake), m_Info.Leverage));
116.                            m_Info.FinanceStop = PointsToFinance(FinanceToPoints(MathAbs(FinanceStop), m_Info.Leverage));
117.                            m_Info.IsDayTrade = true;
118.                            m_Info.IsMaximized = true;
119.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
120.                            CreateWindowRAD(115, 64, 170, 210);
121.                            AdjustTemplate(true);
122.                    }
123. //+------------------------------------------------------------------+
124.            ~C_ChartFloatingRAD()
125.                    {
126.                            ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Chart);
127.                            FileDelete(m_Info.szFileNameTemplate);
128.                            
129.                            delete m_Mouse;
130.                    }
131. //+------------------------------------------------------------------+
132.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
133.                    {
134.                            static int sx = -1, sy = -1;
135.                            int x, y, mx, my;
136.                            static eObjectsIDE it = MSG_NULL;
137.    
138.                            switch (id)
139.                            {
140.                                    case CHARTEVENT_MOUSE_MOVE:
141.                                            if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft))
142.                                            {
143.                                                    switch (it = CheckMousePosition(x = (int)lparam, y = (int)dparam))
144.                                                    {
145.                                                            case MSG_TITLE_IDE:
146.                                                                    if (sx < 0)
147.                                                                    {
148.                                                                            ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
149.                                                                            sx = x - m_Info.x;
150.                                                                            sy = y - m_Info.y;
151.                                                                    }
152.                                                                    if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x = mx);
153.                                                                    if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y = my);
154.                                                                    break;
155.                                                    }
156.                                            }else
157.                                            {
158.                                                    if (it != MSG_NULL)
159.                                                    {
160.                                                            switch (it)
161.                                                            {
162.                                                                    case MSG_MAX_MIN:
163.                                                                            m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
164.                                                                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? 210 : m_Info.Regions[MSG_TITLE_IDE].h + 6));
165.                                                                            break;
166.                                                                    case MSG_DAY_TRADE:
167.                                                                            m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true);
168.                                                                            break;
169.                                                            }
170.                                                            it = MSG_NULL;
171.                                                            AdjustTemplate();
172.                                                    }
173.                                                    if (sx > 0)
174.                                                    {
175.                                                            ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                                                
176.                                                            sx = sy = -1;
177.                                                    }
178.                                            }
179.                                            break;
180.                            }
181.                    }
182. //+------------------------------------------------------------------+
183. };
184. //+------------------------------------------------------------------+

Código fonte da classe C_ChartFloatingRAD

Sei que este código parece muito confuso e complexo, isto para grande parte de quem está começando. Mas se você está acompanhando esta sequencia desde o seu inicio, já deve ter aprendido bastante coisa sobre programação em MQL5. Mas mesmo assim este código acima é bem mais complicado do que muitos costumeiramente criam.

Se você comparar este código com o presente no artigo anterior, irá ver que ele ficou ainda mais complicado. No entanto, grande parte da complicação foi jogada para dentro da classe C_AdjustTemplate, que foi explicada no tópico anterior. Mas vamos então entender o que este código acima faz. Pois é nele que reside a mágica do indicador Chart Trade. Isto por que, o código do indicador, visto no artigo anterior, permanece igual. Mas este daqui mudou, e mudou o suficiente para que novas funcionalidades fossem adicionada ao indicador.

Começando a explicação, na linha 10, temos um enumeração, esta irá nos ajudar a acessar, mais facilmente os objetos presentes no template. Mas nesta mesma enumeração temos um valor, MSG_NULL, este valor está ali por um motivo de controle, mas isto será visto no decorrer da explicação.

Caminhando pelo código, vemos entre as linhas 22 e 25 uma estrutura, esta estará sendo usada por uma variável que é um array. Mas observando o numero de elementos no array, que coisa é esta ?!?! Não temos um valor e sim uma outra coisa. Você pode estar pensando: Não encontro em lugar nenhum o que esta coisa significa. Calma, calma, não precisa entrar em pânico. Este dado, que indica o número de elementos do array, é justamente o último dado da enumeração feita na linha 10. Mas existe um detalhe aqui. Este último valor não representa de fato um elemento, ou objeto. Se ele representa-se, a declaração deveria ser diferente. Mas como não representa, podemos usá-lo da forma como esta sendo feito.

A próxima linha a merecer alguma explicação, se encontra no ponto 54. Aqui é o ponto, onde iremos de fato acessar o template. Mas antes de entrar nesta explicação, vamos ver uma outra coisa. Este procedimento está sendo acessado em dois locais. O primeiro é na linha 121 que é o constructor, e o segundo é na linha 171, que fica no procedimento de tratamento de mensagens. E por que é importante saber disto ? O motivo é o que iremos e queremos fazer no template.

No primeiro caso, que acontece no constructor, queremos montar o template de maneira que ele trabalhe de uma forma especifica. Já no segundo caso, iremos trabalhar com que já temos, não iremos mudar o template, mas iremos fazer com que ele se adeque de forma precisa ao que queremos.

Talvez esta explicação tenha ficado um pouco confusa, mas vamos ver o funcionamento do procedimento da linha 54. Talvez isto lhe ajude a entender melhor as coisas. Entre as linhas 56 e 67, temos a definição de duas macros. Elas existem ali para nos auxiliar, e facilitar na programação. Assim como as linhas 89 e 90, servem para remover tais macros. Normalmente gosto de usar macro, quando irei repetir um mesmo código, ou algum parâmetro, diversas vezes. Neste caso especifico, o que está se repetindo é o parâmetro. Mas o código das macros são bem simples.

A primeira que está entre as linhas 56 e 61, irá adicionar coisas para a classe C_AdjustTemplate, nos informar de volta. Na segunda macro, que está entre as linhas 62 e 67, pegamos os valores que a classe C_AdjustTemplate nos informa e os transformamos em um valor, que é armazenado no array que foi declarado na linha 25. Então, tenha isto em mente. Não estamos simplesmente chutando, onde os objetos estão. Estamos perguntando para o template, onde eles estão.

Com isto na linha 71 verificamos se estamos iniciando o ajuste do template. Se este não for o caso, iremos executar a chamada presente na linha 75. No entanto, se for a primeira chamada, iremos dizer a classe C_AdjustTemplate, quais serão os nomes a serem usados. Isto é feito na linha 73. Agora bastante atenção a linha 74. Veja que nesta linha iremos usar a enumeração a fim de dizer a classe C_AdjustTemplate, quais são os objetos que queremos saber os dados. Usamos um laço para isto. Assim a classe C_AdjustTemplate irá saber quais as propriedades que deverão ser capturas.

De qualquer forma, entre as linhas 76 e 81, estaremos informando ao template, os valores a serem usados nas propriedades dos objetos. Cada linha indica um objeto, a propriedade a ser modificada e o valor a ser usado.

Finalmente na linha 82, dizemos a classe C_AdjustTemplate que pode ajustar o template como informado. Isto é feito conforme foi visto no tópico anterior. Quando todo o trabalho foi executado, iremos verificar na linha 83, se esta sendo feita a primeira chamada. Caso isto seja verdadeiro, iremos e ajustar os valores do array declarado na linha 25. Isto é feito via um laço que diz a classe C_AdjustTemplate, quais são os objetos, e a propriedade, que desejamos saber o valor.

Depois de terminado este trabalho usamos a linha 85 para fechar a classe C_Template. E para finalizar na linha 87, pedimos para que o objeto OBJ_CHART seja atualizado. Desta maneira iremos ver a mágica acontecendo, da mesma forma como você pode ver no vídeo de demonstração, que se encontra mais no final do artigo.

Observem que não estamos testando nenhum tipo de erro aqui. Estamos presumindo que tudo esteja e ocorra bem. Mas iremos testar a presença de erros no código do indicador. Então se acontecer qualquer falha, ela não será tratada aqui, e sim no código do indicador.

Agora vamos ver uma coisa diferente: A linha 93, inicia uma função bastante interessante, já que ela poderia ser colocada diretamente onde seria utilizada. Mas para tornar o código um pouco mais legível, ela foi criada. Esta função conta com um laço, este se inicia na linha 97, e irá varrer cada um dos objetos presentes no OBJ_CHART. Lembre-se do seguinte: O objeto OBJ_CHART, contem o template, e este contem os objetos que estaremos verificando. Durante esta varredura, iremos criar um retângulo que é a caixa, ou região de clique de cada objeto. Isto é feito entre as linhas 99 e 102.

Uma vez que temos esta região de clique, podemos comparar ela com a região informada como sendo os parâmetros da chamada. Esta comparação se dá justamente na linha 103. Agora observe que além da região, também existem algumas condições extras. Se tudo estiver de acordo, o index do objetos será retornado. Caso contrário iremos retornar um valor MSG_NULL. É justamente por este motivo, que precisamos que a enumeração, definida lá no começo, tenha este valor. Caso este não existisse, não seria possível informar que o clique foi em um objeto inválido no indicador Chart Trade.

A próxima coisa a ser explicada esta na linha 132, que é justamente o tratador de eventos. Este agora contém alguma partes novas. Mas é justamente estas partes novas, que torna possível fazer o que é visto no vídeo de demonstração. Então vamos entender com bastante atenção o que esta acontecendo. E observem, que em ponto algum, criamos até o momento, nenhum outro objeto que não seja o OBJ_CHART. E mesmo assim, temos o funcionamento esperado.

Muito do código se parece bastante com o que foi visto no artigo anterior. Mas mesmo assim existem pequenas diferenças, que merecem ser comentadas, isto para que os menos experientes, consigam entender o que esta acontecendo. Nas linhas 134 a 136 definimos algumas variáveis. A variável definida na linha 136 é que nos interessa neste artigo, já que as demais já foram explicadas.

Esta variável presente na linha 136, irá servir de memória para nos. Isto por que não poderemos contar, com nenhuma ajuda extra, vinda do MetaTrader 5, a fim de solucionar a questão dos cliques. Normalmente, quando se tem objetos no gráfico, o MetaTrader 5, irá nos informar o nome do objeto que recebeu o clique. Isto se dá por meio do evento CHARTEVENT_OBJECT_CLICK. Mas aqui, não temos nenhum objeto, real, além do OBJ_CHART. Então qualquer clique que for dado na área do indicador Chart Trade, será informado pelo MetaTrader 5, como um clique no OBJ_CHART.

Mas, o único evento que temos sendo tratado é o CHARTEVENT_MOUSE_MOVE. Isto por ele ser mais que o suficiente para nos. No entanto, os cliques serão tratados apenas e somente se o Indicador Mouse, não estiver em estado de estudo. Isto é testado na linha 141. Caso o indicador Mouse esteja em estado de estudo, ou alguma outra coisa tenha acontecido, iremos para a linha 156. E aqui mora uma questão. Caso a variável declarada na linha 136 esteja com um valor diferente, algo deverá acontecer. Mas primeiro vamos ver quando e onde esta variável irá receber o seu valor.

Quando o indicador mouse, estiver livre e um clique ocorrer. O teste efetuado na linha 141, irá permitir que venhamos a verificar onde, e quem recebeu o clique. Isto é feito na linha 143. Neste momento, iremos informar a função de analise, onde se encontra o mouse no momento do clique. Porém existe uma pequena falha aqui. Mas não irei entrar em detalhes agora, já que ela será corrigida no próximo artigo, assim como outras coisinhas que ainda faltam ser feitas. Bem, ao mesmo tempo que a função verifica, ela retorna o nome do objeto que recebeu o clique, e este é gravado na variável estática.

Agora testamos, mas por questões práticas, testamos apenas e somente o objeto de titulo. Caso ele tenha recebido o clique, a linha 145, irá permitir que o código de arrasto seja executado. Poderíamos colocar outros objetos aqui. No entanto fazer isto iria complicar, pelo menos neste momento, a lógica de testagem. Já que enquanto o botão do mouse estivesse pressionado, o objeto iria ficar recebendo mensagens de clique.

Como foi falado, podemos melhorar isto. Mas por hora quero deixar o código mais simples, fazendo as mudanças aos poucos. Já que o conceito mostrado aqui, é muito diferente do que muitos normalmente programam.

Mas voltemos agora a linha 156. Quando esta linha for executada. Iremos dentro desta condição, efetuar dois testes. O primeiro, irá verificar se algum objeto presente no OBJ_CHART recebeu algum clique. Caso isto tenha acontecido, teremos a mesma condição que seria feita, caso o objeto realmente existisse no gráfico, e o MetaTrader 5, gerasse o evento CHARTEVENT_OBJECT_CLICK. Ou seja, estamos "emulando" o funcionamento de um sistema já existente. Mas de forma a dar um comportamento adequado a todo o Indicador.

Assim, caso o teste presente na linha 158, passe. Iremos primeiramente fazer o tratamento adequado do evento. Isto é feito entre as linhas 160 a 169, para logo depois, na linha 170, remover a indicação de objeto, e na linha 171, fazer uma atualização do status presente no template. Assim tudo o indicador será atualizado, permitindo que você tenha a ilusão de que os objetos estão presentes no gráfico. Sendo que o único real objeto presente é o OBJ_CHART.

Todo o restante do código da classe C_ChartFloatingRAD, já foi explicado no artigo anterior. Desta maneira, não vejo necessidade de comentar novamente ele aqui.

Vídeo de demonstração.

Conclusão

Como você pode ver, neste artigo, apresentei uma forma de usar templates em um OBJ_CHART, de maneira que possamos ter um comportamento, muito similar ao que teríamos se objetos reais estivesse no gráfico. Talvez a maior vantagem em se fazer uso do que estou mostrando, é a agilidade em se construir uma interface, usando elementos presentes no próprio MetaTrader 5, isto sem fazer uso de uma pesada programação via MQL5.

Apesar de parecer bastante confuso e complexo, o que estou mostrando. Isto se deve ao fato de ser algo completamente novo a você, caro leitor. Com o tempo e um pouco de prática, você irá notar que podemos fazer uso massivo deste conhecimento, em diversos tipos de cenários e momentos. Mas devo admitir que o sistema ainda não está completo. E além disto, ele conta com uma pequena falha. Mas isto será resolvido no próximo artigo, onde iremos finalmente permitir que o usuário, digite diretamente os valores no Chart Trade. Esta questão vai ser bem interessante de ser resolvida. Então não perca o próximo artigo desta serie.


Arquivos anexados |
Anexo.zip (420.65 KB)
Dominando o ONNX: Ponto de virada para traders MQL5 Dominando o ONNX: Ponto de virada para traders MQL5
Mergulhe no mundo do ONNX, um poderoso formato aberto para compartilhar modelos de aprendizado de máquina. Descubra como o uso do ONNX pode revolucionar a negociação algorítmica em MQL5, permitindo que os traders integrem sem obstáculos modelos avançados de inteligência artificial e elevem suas estratégias a um novo patamar. Desvende os segredos da compatibilidade entre plataformas e aprenda a desbloquear todo o potencial do ONNX em sua negociação no MQL5. Melhore sua negociação com este guia detalhado sobre ONNX.
A sazonalidade no mercado de moedas e suas possibilidades de uso A sazonalidade no mercado de moedas e suas possibilidades de uso
Todo indivíduo moderno está familiarizado com o conceito de sazonalidade, por exemplo, todos nós estamos acostumados com o aumento dos preços de vegetais frescos no inverno ou o aumento do preço dos combustíveis durante fortes geadas, mas poucos sabem que existem padrões semelhantes no mercado de moedas.
Adicionando um LLM personalizado a um robô investidor (Parte 1): Implantação de equipamentos e ambiente Adicionando um LLM personalizado a um robô investidor (Parte 1): Implantação de equipamentos e ambiente
Os modelos de linguagem são uma parte importante da inteligência artificial que evolui rapidamente, por isso devemos pensar em como integrar LLMs poderosos em nossa negociação algorítmica. Para a maioria das pessoas, é desafiador configurar esses poderosos modelos de acordo com suas necessidades, implementá-los localmente e, em seguida, aplicá-los à negociação algorítmica. Esta série de artigos explorará uma abordagem passo a passo para alcançar esse objetivo.
Modelos de classificação da biblioteca Scikit-learn e sua exportação para o formato ONNX Modelos de classificação da biblioteca Scikit-learn e sua exportação para o formato ONNX
Neste artigo, exploraremos o uso de todos os modelos de classificação do pacote Scikit-learn para resolver o problema de classificação dos íris de Fisher, tentaremos convertê-los para o formato ONNX e usaremos os modelos resultantes em programas MQL5. Também compararemos a precisão dos modelos originais e suas versões ONNX no Iris dataset completo.