English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
preview
Indicadores múltiplos em um gráfico (Parte 05): Transformando o MetaTrader 5 em um sistema RAD (I)

Indicadores múltiplos em um gráfico (Parte 05): Transformando o MetaTrader 5 em um sistema RAD (I)

MetaTrader 5Negociação | 10 março 2022, 12:03
1 246 1
Daniel Jose
Daniel Jose

Introdução

Muita gente não sabe de fato como programar, mas são bem criativas, tendo excelentes ideias, mas a falta de conhecimento ou entendimento sobre programação as proíbe de fazer algumas coisas. Pois bem, que tal você criar sua própria interface Chart Trade, para envio de ordens a mercado, ou para ajustar os parâmetros a serem usadas em ordens pendentes ? E isto sem programar nada, apenas usando as funções que estarão dentro do EA ? Ficou curioso, bem vejam como poderia ficar a sua tela:


Você pode estar pensando: Mas como eu poderia fazer isto ? Eu não sei nada de programação, ou o que sei não será suficiente para me permitir fazer isto. Pois bem, o Chart Trade visto na imagem acima, foi conseguido dentro da própria plataforma MT5 e foi desenhada conforme mostra a figura abaixo:


Agora que você viu o que irei mostrar neste artigo, já deve estar empolgado, e cheio de ideias para montar o seu próprio Chart, mas temos alguns passos para que tudo funcione e uma vez que o código de suporte esteja montado, a sua criatividade será o seu único limitador para o design, de seu próprio IDE Chart Trade. Este artigo é continuação dos artigos anteriores, então para um total e completo entendimento, peço para que vejam os artigos anteriores a este desta mesma serie.

Dito isto, vamos ao trabalho.


Planejamento

Para começar o gráfico que você irá usar como IDE, deverá ter suas propriedades modificadas, isto para reduzir possíveis efeitos colaterais, não que eles irão ocorrer, mas ao deixar o gráfico limpo ficará mais fácil construir e desenhar a interface do Chart Trade, então abra as propriedades do gráfico e deixe as coisas conforme as imagens abaixo.

     

Desta forma a tela irá ficar totalmente limpa, e livre de qualquer coisa que possa atrapalhar durante o design de nossa IDE. Agora vamos entender uma coisa. Nossa IDE será salva como um arquivo de configuração, ou seja, um TEMPLATE, então podemos usar qualquer um dos objetos que o MT5 nos fornece, mas por motivos práticos iremos usar apenas alguns deles, veja em Tipos de Objetos no MT5 todos os disponíveis.

Objeto Tipo de coordenadas usadas para posicionamento Interessante para IDE 
Texto Data e Preço  NÃO
Etiqueta Posição X e Y  SIM
Botão  Posição X e Y  SIM
Gráfico  Posição X e Y  SIM
Bitmap  Data e Preço  NÃO
Etiqueta Bitmap  Posição X e Y  SIM
Editar  Posição X e Y  SIM
Evento  Apenas a Data é usada  NÃO
Rótulo Retangular Posição X e Y  SIM

Por motivos de estarmos usando um sistema que poderá estar em qualquer região da tela, não é prático usar um objeto que não utiliza o sistema de coordenadas X e Y para posicionar as coisas, já que se fizermos isto poderemos ter uma IDE diferente da que criamos, então nos limitamos a 6 objetos, mas eles são mais que o suficiente para que possamos criar uma interface.

A ideia é você posicionar os objetos em uma ordem lógica, da mesma forma como você desenharia algo na tela, primeiro inicie criando um fundo e vá colocando as coisas umas sobre as outras, posicionando e ajustando os objetos conforme a interface vá se desenhando. Vejam como isto se dará:

    

    

Notem que é algo bem simples, é só será necessário um pouco de prática, e você irá dominar está forma de desenhar a sua própria IDE, a ideia aqui parece muito com aquela usada em programas RAD muito usados para criar interface de programas onde a interface com o usuário pode ser muito complexa de ser desenhada via código, não que não podemos criar uma interface diretamente via código, mas usando esta forma é muito mais rápido e facilmente modificável, ou seja algo ideal para quem deseja uma interface com a sua cara.

Quando você termina poderá ter uma interface como a mostrada abaixo, ou ainda mais bacana, mas aqui eu procurei utilizar todos objetos possíveis para que eles fossem testado, mas nada impede que você crie as coisas da forma que melhor lhe agrade.

Esta é a primeira fase da criação de nossa IDE, agora precisaremos criar um código que de fato dê suporte a esta interface e a faça ser funcional, mas o simples fato de você poder desenvolver a sua própria interface customizada, já é motivo para que você se sinta ainda mais motivado e esta motivação irá ser traduzida em forma de um código.

A próxima etapa é salvar esta interface como sendo um arquivo de configuração, você já poderá salvá-la e usando o código da versão anterior será capaz de mostrar ela como se fosse um indicador, isto implica que não precisaremos fazer grandes mudanças no código original, mas se você for verificar a possibilidade de receber eventos, ou enviar eventos para dentro da nossa IDE, verá que isto não é possível. Mas se a interface foi criada usando objetos do próprio MT5, por que não consigo enviar e receber eventos destes objetos ? A resposta não é muito simples de se dar, mas é simples de ser demonstrada. Adicionando o código abaixo a versão original do EA, você poderá verificar isto.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        switch (id)
        {
                case CHARTEVENT_OBJECT_CLICK:
                        Print(sparam);
                        break;
// .... Restante do código ...
        }
}

Este código irá informar o nome do objeto que esta recebendo um clique e gerando um evento, que no caso é o evento CHARTEVENT_OBJECT_CLICK, mas a mensagem impressa será o nome do objeto criado pelo EA e não o nome dos objetos da IDE. Isto pode a primeira vista parecer ser um grande problema, tornando impossível utilizar nossa IDE, mas existe uma solução bem simples. LER o arquivo de configuração e então criar os objetos conforme o indicado neste arquivo, isto irá criar nossa IDE diretamente no gráfico. Então ao se analisar o arquivo de configuração ( TPL ) encontramos os dados que precisamos usar.

Chave Descrição
<chart> Inicia o arquivo de configuração
</chart> Finaliza o arquivo de configuração
<window> Inicia a estrutura de elementos presentes no gráfico
</window> Finaliza a estrutura de elementos presentes no gráfico
<indicator> Inicia a estrutura que informa dados referentes a algum indicador
</indicator> Finaliza a estrutura que informa dados do indicador
<object> Inicia a estrutura que informa dados sobre algum objeto
</object> Finaliza a estrutura que informa dados sobre o objeto

Esta estrutura se parece da seguinte forma dentro do arquivo TPL.

<chart>

.... DADOS

<window>

... DADOS

<indicator>

... DADOS

</indicator>

<object>

... DADOS

</object>

</window>
</chart>

a parte que de fato nos interessa está entre <object> e </object> e pode existir várias destas estruturas, cada uma indicando um objeto único. Então para começar teremos que mudar o local do arquivo, temos que colocar ele em um local que possamos ler ele, e este local é o diretório FILES, você pode mudar o local que eu usei, mas o arquivo deverá estar dentro da arvore FILES. Um detalhe importante, apesar do sistema ter recebido uma modificação que permite limpar o gráfico quando o arquivo de configuração da IDE for utilizado, o ideal é você também ter que ter um arquivo LIMPO com o mesmo nome no diretório Profiles\Templates, isto minimiza ao máximo algum resíduo presente no template default conforme foi visto nos artigos anteriores. As principais mudanças pode ser vista abaixo em destaque:

#include <Auxiliar\Chart IDE\C_Chart_IDE.mqh>
//+------------------------------------------------------------------+
class C_TemplateChart : public C_Chart_IDE
{

 .... Outras partes do código ....

//+------------------------------------------------------------------+
void AddTemplate(const eTypeChart type, const string szTemplate, int scale, int iSize)
{
        if (m_Counter >= def_MaxTemplates) return;
        if (type == SYMBOL) SymbolSelect(szTemplate, true);
        SetBase(szTemplate, (type == INDICATOR ? _Symbol : szTemplate), scale, iSize);
        if (!ChartApplyTemplate(m_handle, szTemplate + ".tpl")) if (type == SYMBOL) ChartApplyTemplate(m_handle, "Default.tpl");
        if (szTemplate == "IDE") C_Chart_IDE::Create(m_IdSubWin);
        ChartRedraw(m_handle);
}
//+------------------------------------------------------------------+
void Resize(void)
{
#define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_Info[c0].szObjName, A, B)
        int x0 = 0, x1, y = (int)(ChartGetInteger(Terminal.Get_ID(), CHART_HEIGHT_IN_PIXELS, m_IdSubWin));
        x1 = (int)((ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS, m_IdSubWin) - m_Aggregate) / (m_Counter > 0 ? (m_CPre == m_Counter ? m_Counter : (m_Counter - m_CPre)) : 1));
        for (char c0 = 0; c0 < m_Counter; x0 += (m_Info[c0].width > 0 ? m_Info[c0].width : x1), c0++)
        {
                macro_SetInteger(OBJPROP_XDISTANCE, x0);
                macro_SetInteger(OBJPROP_XSIZE, (m_Info[c0].width > 0 ? m_Info[c0].width : x1));
                macro_SetInteger(OBJPROP_YSIZE, y);
                if (m_Info[c0].szTemplate == "IDE") C_Chart_IDE::Resize(x0);
        }
        ChartRedraw();
#undef macro_SetInteger
}
//+------------------------------------------------------------------+

... Restante do código

}

Vejam que estamos adicionado a interface IDE como sendo uma nova classe e esta esta sendo herdada pela nossa classe original, ou seja a classe original terá sua funcionalidade aumentada, e isto sem causar nenhum efeito colateral no código original.

Até aqui foi a parte fácil, mas agora temos que fazer algo um pouco mais complicado, mas que dará suporte a nossa IDE. Primeiramente teremos que criar um protocolo de mensagem a ser usado pelo sistema, este protocolo irá permitir que o sistema trabalhe conforme pode ser visto abaixo:


Notem que podemos modificar os dados do sistema, coisa que até o momento não é possível, mas ao adicionar um protocolo de mensagem será possível fazer nossa IDE se tornar funcional, então vamos definir algumas coisas:

Mensagem Intenção
MSG_BUY_MARKET Envia uma ordem de compra a mercado
MSG_SELL_MARKET Envia uma ordem de venda a mercado
MSG_LEVERAGE_VALUE Dado sobre o fator de alavancagem
MSG_TAKE_VALUE Dado sobre o valor de ganho financeiro
MSG_STOP_VALUE Dado sobre o valor de stop financeiro
MSG_RESULT Dado sobre o resultado atual da posição aberta
MSG_DAY_TRADE Informa se a operação encerrará no final do dia ou não

Este protocolo é um passo muito importante, e uma vez definido você deverá fazer uma mudança no arquivo de configuração, então ao se abrir a lista de objetos, deveremos mudar as coisas de forma a ficar como mostrado abaixo:

A interface que estou mostrando irá ter a lista de objetos igual a imagem, notem com muita atenção ao seguinte fato, o NOME dos objetos, vejam que eles correspondem a cada uma das mensagens que estaremos usando. o nome que os demais objetos terão é irrelevante, já eles são usados para ajudar na modelagem da IDE, mas os objetos com os nomes das mensagens, estes irão tanto receber como também enviar mensagens. Se você desejar usar mais ou outros tipos de mensagens, bastará fazer as devidas mudanças no código da classe e o próprio MT5 irá fornecer os meios para que as mensagens sejam trocadas entre a IDE e o código do EA.

Mas ainda assim precisaremos fazer um estudo do arquivo TPL, para saber como criar a nossa classe objeto, e agora vamos procurar saber como os objetos são declarados dentro do arquivo TPL. É verdade que teremos menos acesso as propriedades dos objetos dentro do arquivo TPL do que via programação, já que a própria interface do MT5 nos dá menos acesso as propriedades de cada um dos objetos, mas ainda assim o acesso que teremos já será o suficiente para que nossa IDE se torne funcional.

Bem, dentro do arquivo TPL, vemos a estrutura de nosso interesse, <object> até </object>, mas ao analisar os dados dentro desta estrutura não nos parece claro como saber qual o tipo de objeto referenciado, mas ... observando com calma, entendemos que o tipo de objeto é definido pela variável type, está recebe um valor diferente para cada um dos objetos, depois de um tempo teremos a seguinte tabela, com base nos objetos que queremos usar:

Valor da variável TYPE Objeto referenciado
102 OBJ_LABEL
103 OBJ_BUTTON
106 OBJ_BITMAP_LABEL
107  OBJ_EDIT
110  OBJ_RECTANGLE_LABEL

Bem, nossa classe já poderá começar a tomar forma, então a primeira rotina a ser montada pode ser vista abaixo:

bool Create(int nSub)
{
        m_CountObject = 0;
        if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
        FileReadInteger(m_fp, SHORT_VALUE);
                                
        for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
        m_SubWindow = nSub;
        m_szLine = "";
        while (m_szLine != "</chart>")
        {
                if (!FileReadLine()) return false;
                if (m_szLine == "<object>")
                {
                        if (!FileReadLine()) return false;
                        if (m_szLine == "type")
                        {
                                if (m_szValue == "102") if (!LoopCreating(OBJ_LABEL)) return false;
                                if (m_szValue == "103") if (!LoopCreating(OBJ_BUTTON)) return false;
                                if (m_szValue == "106") if (!LoopCreating(OBJ_BITMAP_LABEL)) return false;
                                if (m_szValue == "107") if (!LoopCreating(OBJ_EDIT)) return false;
                                if (m_szValue == "110") if (!LoopCreating(OBJ_RECTANGLE_LABEL)) return false;
                        }
                }
        }
        FileClose(m_fp);
        return true;
}

Veja que a primeira coisa a ser feita é abrir o arquivo em modo leitura e como um arquivo binário, o fato de fazer isto é para não deixar nada escapar, pois bem, ao usarmos um editor HEXA temos a seguinte visualização do arquivo TPL. Vejam que ele começa com um valor bem curioso.

Parece confuso ?!?! Pois não é, o arquivo segue o sistema UTF-16 para codificar as coisas, e já que sabemos que a organização é feita por linhas, vamos criar uma rotina para ler uma linha inteira de uma única vez, e para isto criamos o seguinte código:

bool FileReadLine(void)
{
        int utf_16 = 0;
        bool b0 = false;
        m_szLine = m_szValue = "";
        for (int c0 = 0; c0 < 500; c0++)
        {
                utf_16 = FileReadInteger(m_fp, SHORT_VALUE);
                if (utf_16 == 0x000D) { FileReadInteger(m_fp, SHORT_VALUE); return true; } else
                if (utf_16 == 0x003D) b0 = true; else
                if (b0) m_szValue = StringFormat("%s%c", m_szValue, (char)utf_16); else m_szLine = StringFormat("%s%c", m_szLine, (char)utf_16);
                if (FileIsEnding(m_fp)) break;
        }
        return (utf_16 == 0x003E);
}

A leitura tenta se dar da forma o mais eficiente quanto for possível, então quando encontramos um sinal de igual ( = ), fazemos a separação já durante a leitura para evitar uma separação a posterior. O loop limita a string a um máximo de 500 caracteres, mas isto foi arbitrário, e pode ser mudado conforme a necessidade. A cada nova linha encontrada a função irá retornar, nos dando assim o conteúdo dela para que possamos proceder a devida analise.

Precisamos de algumas variáveis para dar suporte ao nosso protocolo de mensagens, e estas podem ser vista abaixo:

class C_Chart_IDE
{
        protected:
                enum eObjectsIDE {eRESULT, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP};
//+------------------------------------------------------------------+
#define def_HeaderMSG "IDE_"
#define def_MaxObject eEDIT_STOP + 32
//+------------------------------------------------------------------+
        private :
                int             m_fp,
                                m_SubWindow,
                                m_CountObject;
                string          m_szLine,
                                m_szValue;
                bool            m_IsDayTrade;
                struct st0
                        {
                                string  szName;
                                int     iPosX;
                        }m_ArrObject[def_MaxObject];

// ... Resto do código da classe ....

a definição def_MaxObject indica o numero máximo de objetos que podemos manter, este numero é conseguido com base no numero de mensagens, mais um numero extra de objetos que vamos usar, no caso então teremos o máximo de 40 objetos, mas você pode aumentar isto se for necessário. Os 8 primeiros objetos serão usados para transportar mensagem entre a IDE e o MT5, e o alias destes mensagens é visto na enumeração eObjectsIDE, é importante notar isto em caso de se desejar expandir o sistema ou adaptá-lo a outra coisa.

Esta é somente a primeira parte do sistema de suporte, temos um outro ponto a ser observado, e esta parte é uma constante, que trata justamente do sistema de mensagem de fato, a forma como a linguagem MQL5 trata de constantes é meio que embaraçoso para quem já programa em C / C++, isto por que nestas linguagem, uma constante é declarada no próprio enunciado da variável, mas aqui a forma de se fazer pode tornar o código um pouco mais trabalhoso, mas é algo que podemos conviver já que o uso de tais constantes são raras, a forma de se fazer isto pode ser visto abaixo em destaque.

        public  :
                static const string szMsgIDE[];

// ... Restante do código da classe ....

};
//+------------------------------------------------------------------+
static const string C_Chart_IDE::szMsgIDE[] = {
                                                "MSG_RESULT",
                                                "MSG_BUY_MARKET",
                                                "MSG_SELL_MARKET",
                                                "MSG_DAY_TRADE",
                                                "MSG_CLOSE_POSITION",
                                                "MSG_LEVERAGE_VALUE",
                                                "MSG_TAKE_VALUE",
                                                "MSG_STOP_VALUE"
                                             };
//+------------------------------------------------------------------+

Observem as constantes definidas, elas são exatamente os mesmos valores usados nos nomes dos objetos lá na interface, o sistema foi desenhado para não ser case sensitive, então seja, ele não irá diferenciar entre maiúsculas e minúsculas, tratando todas da mesma forma, mas este comportamento pode ser alterado, apesar de eu desaconselhar isto.

Uma vez concluído estes passos, podemos passar para a próxima etapa, então voltemos para o arquivo TPL e vamos analisar algumas coisas, vejam um fragmento do arquivo abaixo:


Depois de saber qual o tipo de objeto a ser usado, temos uma serie de dados que informa as propriedades do objeto, como o nome, posição, cor, fonte, enfim ... temos que levar estas mesmas propriedades para o nossos objetos internos, já que a coisa é bem repetitiva, podemos criar uma rotina genérica para isto, e está pode ser vista abaixo:

bool LoopCreating(ENUM_OBJECT type)
{
#define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B)
#define macro_SetString(A, B) ObjectSetString(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B)
        int c0;
        bool b0;
        string sz0 = m_szValue;
        while (m_szLine != "</object>") if (!FileReadLine()) return false; else
        {
                if (m_szLine == "name")
                {
                        b0 = false;
                        StringToUpper(m_szValue);
                        for(c0 = eRESULT; (c0 <= eEDIT_STOP) && (!(b0 = (m_szValue == szMsgIDE[c0]))); c0++);
                        c0 = (b0 ? c0 : m_CountObject);
                        m_ArrObject[c0].szName = StringFormat("%s%04s>%s", def_HeaderMSG, sz0, m_szValue);
                        ObjectDelete(Terminal.Get_ID(), m_ArrObject[c0].szName);
                        ObjectCreate(Terminal.Get_ID(), m_ArrObject[c0].szName, type, m_SubWindow, 0, 0);
                }
                if (m_szLine == "pos_x"                 ) m_ArrObject[c0].iPosX = (int) StringToInteger(m_szValue);
                if (m_szLine == "pos_y"                 ) macro_SetInteger(OBJPROP_YDISTANCE    , StringToInteger(m_szValue));
                if (m_szLine == "size_x"                ) macro_SetInteger(OBJPROP_XSIZE        , StringToInteger(m_szValue));
                if (m_szLine == "size_y"                ) macro_SetInteger(OBJPROP_YSIZE        , StringToInteger(m_szValue));
                if (m_szLine == "offset_x"              ) macro_SetInteger(OBJPROP_XOFFSET      , StringToInteger(m_szValue));
                if (m_szLine == "offset_y"              ) macro_SetInteger(OBJPROP_YOFFSET      , StringToInteger(m_szValue));
                if (m_szLine == "bgcolor"               ) macro_SetInteger(OBJPROP_BGCOLOR      , StringToInteger(m_szValue));
                if (m_szLine == "color"                 ) macro_SetInteger(OBJPROP_COLOR        , StringToInteger(m_szValue));
                if (m_szLine == "bmpfile_on"            ) ObjectSetString(Terminal.Get_ID()     , m_ArrObject[c0].szName, OBJPROP_BMPFILE, 0, m_szValue);
                if (m_szLine == "bmpfile_off"           ) ObjectSetString(Terminal.Get_ID()     , m_ArrObject[c0].szName, OBJPROP_BMPFILE, 1, m_szValue);
                if (m_szLine == "fontsz"                ) macro_SetInteger(OBJPROP_FONTSIZE     , StringToInteger(m_szValue));
                if (m_szLine == "fontnm"                ) macro_SetString(OBJPROP_FONT          , m_szValue);
                if (m_szLine == "descr"                 ) macro_SetString(OBJPROP_TEXT          , m_szValue);
                if (m_szLine == "readonly"              ) macro_SetInteger(OBJPROP_READONLY     , StringToInteger(m_szValue) == 1);
                if (m_szLine == "state"                 ) macro_SetInteger(OBJPROP_STATE        , StringToInteger(m_szValue) == 1);
                if (m_szLine == "border_type"           ) macro_SetInteger(OBJPROP_BORDER_TYPE  , StringToInteger(m_szValue));
        }
        m_CountObject += (b0 ? 0 : (m_CountObject < def_MaxObject ? 1 : 0));
        return true;
                        
#undef macro_SetString
#undef macro_SetInteger
}

Cada objeto irá receber um nome, e será armazenado no local adequando, mas a linha destacada mostra uma coisa diferente. Quando criarmos a IDE, ela deverá iniciar no topo do gráfico, canto esquerdo superior, mas  esta posição X não necessariamente será o canto esquerdo superior da sub janela, esta posição deverá corresponder ao canto superior esquerdo do objeto OBJ_CHART a qual a IDE estará vinculado, e este objeto é indicado ao carregar o template da IDE, podendo assim estar em qualquer ponto dentro da sub janela, se não corrigirmos isto a IDE não será apresentada no local correto, então armazenamos o valor X e vamos usá-lo depois para apresentar o objeto no local correto, a rotina que faz a correta apresentação da IDE pode ser vista abaixo.

As principais informações usadas nos objetos, já se encontram definidas, mas se for necessário adicionar alguma outra, bastará acrescentar ela a serie de comandos e modificar a propriedade com o valor correspondente.

void Resize(int x)
{
        for (int c0 = 0; c0 < m_CountObject; c0++)
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX);
};

Antes de ver como as mensagens são tratadas, temos que ver duas outras rotinas igualmente importantes, já que o sistema pode esta recebendo valores do EA, que são adquiridos durante a inicialização, e estes valores tem que ser corretamente apresentados e ajustados de forma que se você for usar o Chart Trade possa configurar as ordens diretamente nele, seja para enviar uma ordem a mercado ou uma ordem pendente, isto sem precisar recorrer ao EA como acontecia anteriormente, ambas rotinas são vistas abaixo:

void UpdateInfos(bool bSwap = false)
{
        int nContract, FinanceTake, FinanceStop;

        nContract       = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_LEVERAGE].szName, OBJPROP_TEXT));
        FinanceTake = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_TAKE].szName, OBJPROP_TEXT));
        FinanceStop = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_STOP].szName, OBJPROP_TEXT));
        m_IsDayTrade = (bSwap ? (m_IsDayTrade ? false : true) : m_IsDayTrade);
        ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eCHECK_DAYTRADE].szName, OBJPROP_STATE, m_IsDayTrade);
        NanoEA.Initilize(nContract, FinanceTake, FinanceStop, clrNONE, clrNONE, clrNONE, m_IsDayTrade);
}
//+------------------------------------------------------------------+
void InitilizeChartTrade(int nContracts, int FinanceTake, int FinanceStop, color cp, color ct, color cs, bool b1)
{
        NanoEA.Initilize(nContracts, FinanceTake, FinanceStop, cp, ct, cs, b1);
        if (m_CountObject < eEDIT_STOP) return;
        ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_LEVERAGE].szName, OBJPROP_TEXT, IntegerToString(nContracts));
        ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_TAKE].szName, OBJPROP_TEXT, IntegerToString(FinanceTake));
        ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_STOP].szName, OBJPROP_TEXT, IntegerToString(FinanceStop));
        ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eCHECK_DAYTRADE].szName, OBJPROP_STATE, m_IsDayTrade = b1);
}

Notem que a IDE se liga ao sistema de ordens, então as mudanças feitas no sistema irá refletir no sistema de ordens, desta forma não iremos precisar ficar mudando os dados no EA, como era feito antes, agora podemos fazer isto diretamente na IDE, ou nosso Chart Trade, e isto é feito por meio destas 2 rotinas acima aliadas ao sistema de mensagem que veremos agora.

void DispatchMessage(int iMsg, string szArg, double dValue = 0.0)
{
        if (m_CountObject < eEDIT_STOP) return;
        switch (iMsg)
        {
                case CHARTEVENT_CHART_CHANGE:
                        if (szArg == szMsgIDE[eRESULT])
                        {
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_BGCOLOR, (dValue < 0 ? clrLightCoral : clrLightGreen));
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_TEXT, DoubleToString(dValue, 2));
                        }
                        break;
                case CHARTEVENT_OBJECT_CLICK:
                        if (StringSubstr(szArg, 0, StringLen(def_HeaderMSG)) != def_HeaderMSG) return;
                        szArg = StringSubstr(szArg, 9, StringLen(szArg));
                        StringToUpper(szArg);
                        if ((szArg == szMsgIDE[eBTN_SELL]) || (szArg == szMsgIDE[eBTN_BUY])) NanoEA.OrderMarket(szArg == szMsgIDE[eBTN_BUY]);
                        if (szArg == szMsgIDE[eBTN_CANCEL])
                        {
                                NanoEA.ClosePosition();
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false);
                        }
                        if (szArg == szMsgIDE[eCHECK_DAYTRADE]) UpdateInfos(true);
                        break;
                case CHARTEVENT_OBJECT_ENDEDIT:
                        UpdateInfos();
                        break;
        }
}

Ai você pergunta: Mas é só isto ?!?! Sim, este é o sistema de mensagens que permite o MT5 se comunicar com a IDE, é algo bem simples devo confessar, mas sem esta rotina a IDE não funcionaria e o sistema não poderia ser construído. Pode parecer meio complicado a forma de fazer isto funcionar no EA, mas não é, graças a programação OOP, o código do EA se manterá super simples, a única coisa mais complicada um pouco é a atualização do valor de resultado que irá aparecer na IDE, isto deve ser feito pela rotina OnTick mas para facilitar eu fiz uso dos dados fornecidos pelo MT5 então a rotina ficou assim, esta parte é a mais critica, já que esta rotina é a mais requisitada de todas, então ela tem que ser igualmente a mais rápida.

void OnTick()
{
        SubWin.DispatchMessage(CHARTEVENT_CHART_CHANGE, C_Chart_IDE::szMsgIDE[C_Chart_IDE::eRESULT], NanoEA.CheckPosition());
}

ou seja, a cada nova cotação, uma mensagem é enviada para a classe e esta atualiza o valor resultante na operação, lembre-se esta rotina tem que ser bem otimizada, senão você poderá ter sérios problemas.


Conclusão

As vezes parece impossível fazer algumas coisas, mas eu gosto de desafios, e este de mostrar como fazer um sistema RAD dentro de uma plataforma que originalmente não foi desenvolvida para isto, foi bastante interessante. Espero que este sistema que começou com algo simples e singelo possa lhe motivar a tentar ir onde poucos de fato vão.

Em breve irei trazer novidade para este EA, mas por enquanto, DIVIRTAM-SE ...



Arquivos anexados |
EA_1.04.zip (3275.47 KB)
Últimos Comentários | Ir para discussão (1)
Guilherme Mendonca
Guilherme Mendonca | 13 mai 2022 em 14:48

Parabéns Daniel, 


Estou acompanhando de perto seus artigos e fico muito agradecido por compartilhar seu conhecimento e seus códigos.


Sou programador de MQL5 tem 4 anos e seu artigo vai ser muito útil para todos da comunidade, principalmente porque esta parte gráfica (GUI) no MQL5, temos pouco suporte e pouca base de código para nos auxiliar.


Abs,
Guilherme.

Indicadores múltiplos em um gráfico (Parte 06): Transformando o MetaTrader 5 em um sistema RAD (II) Indicadores múltiplos em um gráfico (Parte 06): Transformando o MetaTrader 5 em um sistema RAD (II)
No artigo anterior mostrei como criar um Chart Trade usando os objetos do MetaTrader 5, transformando a plataforma em um sistema RAD, o sistema funciona muito bem, e acredito que muitos tenham pensado em criar uma biblioteca para ter cada vez mais funcionalidade no sistema proposto, e assim conseguir desenvolver um EA que seja mais intuitivo ao mesmo tempo que tenha uma interface mais agradável e simples de usar.
Gráficos na biblioteca DoEasy (Parte 91): eventos de objetos gráficos padrão no programa Histórico de alterações de nome do objeto Gráficos na biblioteca DoEasy (Parte 91): eventos de objetos gráficos padrão no programa Histórico de alterações de nome do objeto
No artigo, modificaremos a funcionalidade básica para fornecer controle de eventos de objetos gráficos a partir de um programa baseado na biblioteca. Vamos começar criando uma funcionalidade para armazenar o histórico de alterações das propriedades dos objetos gráficos usando a propriedade "Nome do objeto" como exemplo.
Avaliação visual de resultados de otimização Avaliação visual de resultados de otimização
Neste artigo discutiremos como plotar todas passagens das otimizações e como selecionar o critério ótimo personalizado. Além disso, falaremos sobre como programarmos o que quisermos, simplesmente recorrendo a um conhecimento mínimo em MQL5, a uma grande vontade, ao uso dos artigos do site e aos comentários do fórum.
Gráficos na biblioteca DoEasy (Parte 90): eventos de objetos gráficos padrão Funcionalidade básica Gráficos na biblioteca DoEasy (Parte 90): eventos de objetos gráficos padrão Funcionalidade básica
No artigo de hoje criaremos a funcionalidade base para rastrear eventos de eventos de objetos gráficos padrão. Vamos começar com o clique duplo do mouse sobre o objeto gráfico.