preview
Desenvolvendo um sistema de Replay (Parte 46): Projeto do Chart Trade (V)

Desenvolvendo um sistema de Replay (Parte 46): Projeto do Chart Trade (V)

MetaTrader 5Exemplos | 10 abril 2024, 14:17
343 0
Daniel Jose
Daniel Jose

Introdução

No artigo anterior, Desenvolvendo um sistema de Replay (Parte 45): Projeto do Chart Trade (IV), mostrei como fazer para se ter um inicio de funcionalidade no indicador Chart Trade. Mas você deve ter notado que no anexo daquele artigo, existia muitos arquivos a serem transportados. O risco de cometermos algum erro, seja esquecendo de adicionar um arquivo, ou apagar de forma errônea algo importante, torna aquele método pouco interessante.

Sei que muitos fazem uso, exatamente daquela forma de distribuir e guardar as coisas. Mas existe uma maneira bem mais adequada. Pelo menos no que diz respeito a distribuição de executáveis e armazenamento dos mesmo.

A forma que irei explicar aqui, pode vim a lhe ser de grande ajuda. Já que você pode usar o próprio MetaTrader 5 como sendo um grande ajudante, assim como o MQL5. Não é algo lá tão complexo, ou difícil de ser entendido. Mas compreender e saber como tirar proveito do que irei explicar neste artigo. Pode e muito, lhe ajudar a não ter problemas em guardar adequadamente os executáveis, ou qualquer coisa dos quais eles necessitam para seu correto funcionamento.

 

Compreendendo a ideia

Antes de iniciar é preciso, que você compreenda que existem problemas e limitações para se fazer isto. Tais limitações decorrem do fato para o qual, tanto a linguagem MQL5, quanto a própria plataforma MetaTrader 5, não é voltada para ser utilizada de determinadas maneira.

A linguagem, assim como a plataforma é voltada para ser utilizada como meio de podermos visualizar o mercado. O fato de desejarmos, ou tentarmos fazer com que ela trabalhe de maneira diferente, não necessariamente indica que ela é incapaz de fazer tais coisas.

Esta é a primeira das limitações que existe, e da qual você deve estar ciente. O próximo ponto, a ser observado, é o fato de que você pode adicionar quase qualquer coisa nos executáveis. Atenção a isto, eu disse QUASE. Em tese você pode adicionar virtualmente qualquer coisa, desde que saiba como fazer para adicionar, ou embutir as coisas no executável. Naturalmente, a forma de você embutir informações no executável, é os tornando um recurso interno do próprio executável.

Notoriamente, você irá ver muitos adicionando imagens, sons, ou outras coisas do tipo. Mas você conseguiria adicionar um arquivo de template, de forma que ele possa fazer parte do executável? Observando a documentação, você irá ver que a mesma diz que isto, não é possível. E de fato, se você tentar fazer isto, o compilador irá gerar um erro no momento que você tentar compilar o programa.

#define def_IDE_RAD  "Files\\Chart Trade\\IDE_RAD.tpl"
#resource "\\" + def_IDE_RAD;

Se você tentar compilar um executável, que contém o código acima, irá ver que o compilador irá gerar um erro, isto por conta que a definição estará se referindo a um template. O que via de regra é proibido de ser adicionado a um executável que será utilizado na plataforma MetaTrader 5.

Este tipo de limitação, é bastante desagradável, devo confessar, mas é algo que podemos contornar facilmente, mas isto não vem de graça. Existem limitação e problemas a serem resolvido. Isto por que você pode, e irei mostrar como fazer, adicionar um template em um executável, que foi compilado pelo MetaEditor. Mas o grande problema não é embutir o template no executável. O real problema é conseguir usar este mesmo template.

E por que estou querendo explicar como fazer isto? Não seria mais simples, usar as coisas como de costume? Sim, de fato usar as coisas como já estamos acostumados é muito mais simples. No entanto, como foi dito no início deste artigo, é muito mais prático, você ter tudo embutido no executável. Pense só no trabalho em ter que empacotar todos arquivos necessários para executar uma determinada função no MetaTrader 5. Transferir isto para a máquina que será usada para de fato operar no mercado. Se esquecer de algo, tem que voltar e pegar o arquivo, ou arquivos esquecidos. Tem que os colocar na posição correta na arvore de diretórios a fim de que a aplicação consiga acessar os tais arquivos.

Isto ao meu ver é algo bastante cansativo. Uma coisa que muitos fazem, é usar a mesma máquina, tanto para desenvolver, quanto para usar o MetaTrader 5. A estas pessoas, gostaria de falar, que isto é um grande, para não dizer outra coisa, de um erro. Você NUNCA, mas NUNCA mesmo, deve usar a mesma máquina ou instalação do MetaTrader 5, para operar e desenvolver. Isto por conta que ao fazer tal coisa, você pode acabar criando algum tipo de falha, ou brecha na aplicação que faz com que, em caso de estar usando um EA automático, este faça coisas estranha.

Eu mesmo, as vezes costumo ver coisas bem bizarras acontecendo durante a fase de desenvolvimento. As vezes um programa, ou aplicação simplesmente começa a interagir com uma outra aplicação, que está presente no gráfico, fazendo com que as coisas funcionem de maneira bastante bizarra. Algumas vezes é gerado erros, em outras acontecem coisas inexplicáveis. Alguns vão dizer que é por conta de que estou fazendo testes. Ok, tudo bem. Algumas vezes sim. É por motivos de testes, mas em outras, as coisas simplesmente não fazem o menor sentido.

Por isto é imprescindível que você use duas instalações, para de fato fazer uso do MetaTrader 5. Uma para desenvolvimento, e outra apenas para operar no mercado. Isto caso você seja um desenvolvedor. Em qualquer outro caso isto não será de fato necessário.

Pensei muito se deveria ou não mostrar, como fazer certas coisas. Já que a maior parte, de fato não consegue entender o básico sobre MQL5, ou como o MetaTrader 5 funciona. Imagina então entender o que irei mostrar. Alguns vão achar que sou louco, ou um fanfarrão. Mas o fato é que você pode comparar o anexo, presente, neste artigo com o presente no anterior. Isto tanto na forma como eles funcionam, como na própria portabilidade. Na questão do funcionamento, ambos são idênticos. Já na questão da portabilidade. Acredito que este é bem mais. Já que tudo que é preciso fazer é enviar 1 único arquivo. Não precisando se preocupar com estruturas de diretório.

Importante: Apesar de falar que não é preciso se preocupar com a estrutura de diretórios. Isto somente se aplica ao fato de você manter o executável, exatamente no diretório em que ele se encontra. Mudar ele de diretório, ou mesmo mudar o nome do mesmo, fará com que o sistema não funcione adequadamente.

Então vamos ver como a coisa foi de fato implementada, e como você pode fazer para tirar proveito disto em seus próprios códigos.


Recursos, recursos e recursos

Para fazer uso desta modelagem que passaremos a usar, será preciso fazer algumas mudanças no código. Bem, mas apesar disto, o código fonte do indicador não foi modificado. Sendo assim ele não irá aparecer aqui no artigo. Já o código da classe C_ChartFloatingRAD, sofreu algumas pequenas mudanças. Mas como as mudanças não foram em toda a classe. Irei focar apenas no fragmento, onde de fato a mudança se deu.

O fragmento em questão pode ser visto logo abaixo, para logo em seguida termos a explicação do mesmo.

068. inline void AdjustTemplate(const bool bFirst = false)
069.                    {
070. #define macro_AddAdjust(A) {                   \
071.            (*Template).Add(A, "size_x", NULL); \
072.            (*Template).Add(A, "size_y", NULL); \
073.            (*Template).Add(A, "pos_x", NULL);  \
074.            (*Template).Add(A, "pos_y", NULL);  \
075.                            }
076. #define macro_GetAdjust(A) {                                                                        \
077.            m_Info.Regions[A].x = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_x"));  \
078.            m_Info.Regions[A].y = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_y"));  \
079.            m_Info.Regions[A].w = (int) StringToInteger((*Template).Get(EnumToString(A), "size_x")); \
080.            m_Info.Regions[A].h = (int) StringToInteger((*Template).Get(EnumToString(A), "size_y")); \
081.                            }
082. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade
083.                            
084.                            C_AdjustTemplate *Template;
085.                            
086.                            if (bFirst)
087.                            {
088.                                    Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true);
089.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_AddAdjust(EnumToString(c0));
090.                                    AdjustEditabled(Template, true);
091.                            }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
092.                            m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage);
093.                            m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage));
094.                            m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage));
095.                            (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
096.                            (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage));
097.                            (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2));
098.                            (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2));
099.                            (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0"));
100.                            (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
101.                            (*Template).Execute();
102.                            if (bFirst)
103.                            {
104.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_GetAdjust(c0);
105.                                    m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x;
106.                                    AdjustEditabled(Template, false);
107.                            };
108.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? 210 : m_Info.Regions[MSG_TITLE_IDE].h + 6));
109.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx));
110.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny));
111. 
112.                            delete Template;
113.                            
114.                            ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
115.                            ChartRedraw(m_Info.WinHandle);
116. 
117. #undef macro_PointsToFinance
118. #undef macro_GetAdjust
119. #undef macro_AddAdjust
120.                    }

Fragmento do código fonte da classe C_ChartFloatingRAD

Neste fragmento acima, você pode não estar notando nenhuma grande diferença. Mas sim elas existem. De forma bastante sútil a diferença se encontra exatamente na linha 88. Então se você pegar o código da classe C_ChartFloatingRAD, que se encontra no artigo anterior, e exatamente na linha 88 a mudar como mostrado neste fragmento aqui, poderá fazer uso da nova modelagem de dados, que iremos utilizar.

Você pode notar que diferente do código original, neste estamos definindo apenas uma string. E por que disto ? O motivo é que agora iremos utilizar não mais um template como era antes. Agora iremos utilizar um recurso. Ou melhor dizendo, agora o template irá estar embutido no executável do indicador. Por conta disto não precisamos mais informar muitos mais dados.

No entanto esta mudança já logo irá gerar um questionamento. Como iremos usar um template como sendo um recurso. Se não podemos de fato compilar um template como sendo parte de um executável. Ou seja embutir ele como um recurso ? Na verdade, podemos sim, embutir qualquer coisa em um executável. O grande detalhe é: Como devemos de fato fazer isto ?

Para compreender esta questão, é preciso olhar o código da classe C_AdjustTemplate. Isto a fim de conseguir compreender, o por que foi feita a mudança na linha 88 da classe C_ChartFloatingRAD. Bem, o código da classe C_AdjustTemplate, pode ser visto na integra logo abaixo. Já que nele aconteceram mais mudanças, apesar de tudo elas não foram tão grandes, mas mesmo assim será interessante, entender o que de fato aconteceu.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PATH_BTN "Images\\Market Replay\\Chart Trade"
007. #define def_BTN_BUY  def_PATH_BTN + "\\BUY.bmp"
008. #define def_BTN_SELL def_PATH_BTN + "\\SELL.bmp"
009. #define def_BTN_DT   def_PATH_BTN + "\\DT.bmp"
010. #define def_BTN_SW   def_PATH_BTN + "\\SW.bmp"
011. #define def_BTN_MAX  def_PATH_BTN + "\\MAX.bmp"
012. #define def_BTN_MIN  def_PATH_BTN + "\\MIN.bmp"
013. #define def_IDE_RAD  "Files\\Chart Trade\\IDE_RAD.tpl"
014. //+------------------------------------------------------------------+
015. #resource "\\" + def_BTN_BUY
016. #resource "\\" + def_BTN_SELL
017. #resource "\\" + def_BTN_DT
018. #resource "\\" + def_BTN_SW
019. #resource "\\" + def_BTN_MAX
020. #resource "\\" + def_BTN_MIN
021. #resource "\\" + def_IDE_RAD as string IdeRad;
022. //+------------------------------------------------------------------+
023. class C_AdjustTemplate
024. {
025.    private :
026.            string m_szName[],
027.                   m_szFind[],
028.                   m_szReplace[],
029.                   m_szFileName;
030.            int    m_maxIndex,
031.                   m_FileIn,
032.                   m_FileOut;
033.            bool   m_bFirst;
034. //+------------------------------------------------------------------+
035.    public  :
036. //+------------------------------------------------------------------+
037.            C_AdjustTemplate(const string szFile, const bool bFirst = false)
038.                    :m_maxIndex(0),
039.                     m_szFileName(szFile),
040.                     m_bFirst(bFirst),
041.                     m_FileIn(INVALID_HANDLE),
042.                     m_FileOut(INVALID_HANDLE)
043.                    {
044.                            ResetLastError();                               
045.                            if (m_bFirst)
046.                            {
047.                                    int handle = FileOpen(m_szFileName, FILE_TXT | FILE_WRITE);
048.                                    FileWriteString(handle, IdeRad);
049.                                    FileClose(handle);
050.                            }
051.                            if ((m_FileIn = FileOpen(m_szFileName, FILE_TXT | FILE_READ)) == INVALID_HANDLE)        SetUserError(C_Terminal::ERR_FileAcess);
052.                            if ((m_FileOut = FileOpen(m_szFileName + "_T", FILE_TXT | FILE_WRITE)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess);
053.                    }
054. //+------------------------------------------------------------------+
055.            ~C_AdjustTemplate()
056.                    {
057.                            FileClose(m_FileIn);
058.                            FileClose(m_FileOut);
059.                            FileMove(m_szFileName + "_T", 0, m_szFileName, FILE_REWRITE);
060.                            ArrayResize(m_szName, 0);
061.                            ArrayResize(m_szFind, 0);
062.                            ArrayResize(m_szReplace, 0);
063.                    }
064. //+------------------------------------------------------------------+
065.            void Add(const string szName, const string szFind, const string szReplace)
066.                    {
067.                            m_maxIndex++;
068.                            ArrayResize(m_szName, m_maxIndex);
069.                            ArrayResize(m_szFind, m_maxIndex);
070.                            ArrayResize(m_szReplace, m_maxIndex);
071.                            m_szName[m_maxIndex - 1] = szName;
072.                            m_szFind[m_maxIndex - 1] = szFind;
073.                            m_szReplace[m_maxIndex - 1] = szReplace;
074.                    }
075. //+------------------------------------------------------------------+
076.            string Get(const string szName, const string szFind)
077.                    {
078.                            for (int c0 = 0; c0 < m_maxIndex; c0++) if ((m_szName[c0] == szName) && (m_szFind[c0] == szFind)) return m_szReplace[c0];
079.                            
080.                            return NULL;
081.                    }
082. //+------------------------------------------------------------------+
083.            void Execute(void)
084.                    {
085.                            string sz0, tmp, res[];
086.                            int count0 = 0, i0;
087.                                                            
088.                            if ((m_FileIn != INVALID_HANDLE) && (m_FileOut != INVALID_HANDLE)) while ((!FileIsEnding(m_FileIn)) && (_LastError == ERR_SUCCESS))
089.                            {
090.                                    sz0 = FileReadString(m_FileIn);
091.                                    if (sz0 == "<object>") count0 = 1;
092.                                    if (sz0 == "</object>") count0 = 0;
093.                                    if (count0 > 0) if (StringSplit(sz0, '=', res) > 1)
094.                                    {
095.                                            if ((m_bFirst) && ((res[0] == "bmpfile_on") || (res[0] == "bmpfile_off")))
096.                                                    sz0 = res[0] + "=\\Indicators\\Replay\\Chart Trade.ex5::" + def_PATH_BTN + res[1];
097.                                            i0 = (count0 == 1 ? 0 : i0);
098.                                            for (int c0 = 0; (c0 < m_maxIndex) && (count0 == 1); i0 = c0, c0++) count0 = (res[1] == (tmp = m_szName[c0]) ? 2 : count0);
099.                                            for (int c0 = i0; (c0 < m_maxIndex) && (count0 == 2); c0++) if ((res[0] == m_szFind[c0]) && (tmp == m_szName[c0]))
100.                                            {
101.                                                    if (StringLen(m_szReplace[c0])) sz0 =  m_szFind[c0] + "=" + m_szReplace[c0];
102.                                                    else m_szReplace[c0] = res[1];
103.                                            }
104.                                    }
105.                                    FileWriteString(m_FileOut, sz0 + "\r\n");
106.                            };
107.                    }
108. //+------------------------------------------------------------------+
109. };
110. //+------------------------------------------------------------------+

Código fonte da classe C_AdjustTemplate

Este código acima, pode parecer estranho para grande parte de quem está começando. Principalmente para quem não programa de forma profissional. Mas apesar de ele ser bem extravagante, ele consegue fazer com que não precisemos de fato, ter que transferir os arquivos como estava sendo feito no artigo anterior. Aqui estamos embutindo tudo, dentro do executável. Mas isto não vem de graça. Temos um custo ao fazer isto. E o principal custo é: Não podemos mudar o diretório, ou o nome do executável, uma vez que ele tenha sido compilado. Até podemos, mas para isto será preciso fazer outras coisas, das quais não irei explicar no momento, como proceder.

Observem com atenção as definições entre as linhas 6 e a linha 13. Notem que em sua grande maioria, temos algo que nos é bastante familiar a um bom tempo. Observem com mais atenção, a linha 13. Trata-se de um template. O mesmo template que era utilizado antes. Mas agora iremos fazer com que ele deixe de ser um arquivo à parte, e comece a fazer parte do executável. E a forma de fazer isto, é vista logo abaixo, nas próximas linhas.

Notem que entre as linhas 15 e 20, fazemos as definições como é de costume fazer. Isto quando desejamos adicionar imagens, ou sons. Mas na linha 21 temos algo diferente. Algo realmente muito pouco visto, normalmente em códigos. Isto se deve ao fato de que normalmente não fazemos uso de aliás, ou apelidos em uma programação no MQL5, quando vamos embutir recursos nos executáveis. Para ter uma ideia do que estará acontecendo, você pode olhar na documentação RECURSOS. Mas além do que está lá, você também precisa entender alguns outros detalhes. Isto para que a compreensão seja completa.

Apelidos, ou aliás, são coisa muito comuns em alguns tipos de linguagens. Como por exemplo no Visual Basic, ou no VBA ( Visual Basic for Applications ) muito usada no Excel. Tais apelidos, servem como uma forma de podemos acessar as coisas de uma maneira um pouco diferente. Normalmente quando fazemos o acesso a um recurso usamos " :: " que muitas das vezes é usado como solucionador de escopo. Mas quando usamos ele a fim de acessar um recurso, normalmente usamos o nome de definição do recurso. Isto parece complicado, mas é muito mais simples do que parece. Para compreender veja o fragmento abaixo:

01. #define def_BTN_BUY  "Images\\Market Replay\\Chart Trade\\BUY.bmp"
02. #define def_BTN_SELL "Images\\Market Replay\\Chart Trade\\SELL.bmp"
03. #resource "\\" + def_BTN_BUY
04. #resource "\\" + def_BTN_SELL
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     long id;
09.     string sz;
10.     
11.     ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0);
12.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "::" + def_BTN_BUY);
13.     ResourceSave("::" + def_BTN_SELL, "BTN_SELL.bmp");
14.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp");
15.     
16.     return INIT_SUCCEEDED;
17. }
18. //+------------------------------------------------------------------+

Fragmento 01 - Exemplo de usos

Neste fragmento 01, podemos ver e entender melhor, como as coisas funcionam, no primeiro nível de uso de recursos. Então esqueça por alguns momentos a classe C_AdjustTemplate. Vamos entender primeiro, como usar de forma correta os recursos a fim de transportar mais facilmente os nossos códigos já compilados.

As linhas 1 e 2, definimos duas strings. O conteúdo apontado por elas, serão compilados e embutidos no executável, pelo compilador, por conta das linhas 3 e 4. Até neste ponto, não temos nada de grandioso, ou complicado. Estamos trabalhando da mesma forma como seria de costume fazer.

Então na linha 11, dizemos que faremos a criação de um objeto. Neste caso um Bitmap. Novamente, nada de espetacular. Mas agora é que vem a primeira fase, onde iremos fazer uso dos recursos embutidos no executável. Observem a linha 12. Nesta linha estamos usando o tal "::" para dizer que iremos fazer uso de um recurso. No caso um recurso que se encontra presente no executável. Poderíamos estar nos referindo a um outro programa também, mas para não complicar, vamos primeiro entender este conceito mais simples. Ao ler esta linha 12, o compilador irá de fato entender o seguinte:

12.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "::Images\\Market Replay\\Chart Trade\\BUY.bmp");

Mas, se você olhar o conteúdo das strings no objeto. Não irá ver o que está sendo mostrado acima. Irá ver outra coisa. Mas por enquanto, vamos continuar no básico. Isto para entender as coisas de fato.

Legal. Esta linha 12 foi simples de entender. Mas e quanto as demais linhas logo na sequencia ? Pois bem, agora começa as diferenças. Normalmente você não verá em códigos tais coisas. Mas é importante que você as entenda para de fato entender o indicador Chart Trade.

A linha 13 irá pegar o recurso que está sendo apontado, e o salvar com o nome e na localização indicada. Então para o compilador a linha 13, na verdade seria algo do tipo:

13.     ResourceSave("::Images\\Market Replay\\Chart Trade\\SELL.bmp", "\\Files\\BTN_SELL.bmp");

Novamente, aqui estou usando o conceito mais básico. A coisa é um pouco mais complicada. Mas você pode notar uma coisa nesta chamada ResourceSave. O recurso presente no executável, será salvo como sendo um arquivo convencional. Basicamente seria o equivalente a usar a função FileMove. Ou seja, entenda a linha 13 como se fosse algo assim:

FileMove("::Images\\Market Replay\\Chart Trade\\SELL.bmp", 0, "\\Files\\BTN_SELL.bmp", FILE_REWRITE);

Pois é assim mesmo que as coisas funcionariam na prática.

Então na linha 14, ao invés de acessar um recurso interno do executável. Nós iremos fazer uso indireto, do recurso. Isto por que salvamos o mesmo em um arquivo, na linha 13, e agora apontamos para ele, na linha 14. Mas preste atenção a isto. Diferente da linha 12, na linha 14, estamos de fato usando um arquivo físico, presente no disco. Este irá permanecer ali, até que seja apagado ou modificado.

O uso deste tipo de coisa, presente nas linhas 13 e 14, não é muito comum em programas MQL5. Normalmente lançamos o recurso no executável, e o usamos diretamente dentro do executável. É assim que de fato fazemos as coisas. Mas como falei antes a história é um pouco mais complicada. No entanto, interessante. Você não precisa de fato colocar os recursos em cada um de seus executáveis. Mesmo por que isto tornaria a coisa muito complicada em termos de padronização. Você pode fazer uso de algo muito comum em programação. Usar um arquivo para os recursos. Normalmente, no caso do Windows, isto seria colocado em DLL. Assim podemos padronizar um pouco as coisas.

No MQL5, você pode fazer algo parecido. Mas tenha em mente, que é preciso fazer algum tipo de padronização. Caso contrário, você irá gerar uma quantidade monstruosa de dados inúteis.

E como podemos fazer isto no MQL5 ? Simples. Bastará indicar o nome do arquivo, no momento que for fazer uso do recurso. Lembra, que informei, o fato de que as coisas eram bem mais complicadas. É justamente isto, Ali, eu estava ignorando o nome do executável. Então vamos supor que você tenha um executável, com o nome de ICONS.LIB. E que nele esteja as mesmas imagens, do fragmento 01. E que este executável, ICONS.LIB estivesse na raiz da pasta Indicators. Então para fazer a mesma coisa, só que agora usando o ICONS.LIB, o fragmento 01, ficaria como mostrado abaixo:

01. //+------------------------------------------------------------------+
02. #define def_BTN_BUY  "Images\\Market Replay\\Chart Trade\\BUY.bmp"
03. #define def_BTN_SELL "Images\\Market Replay\\Chart Trade\\SELL.bmp"
04. #define def_LIB      "\\Indicators\\Icons.Lib"
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     long id;
09.     string sz;
10.     
11.     ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0);
12.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, def_LIB + "::" + def_BTN_BUY);
13.     ResourceSave(def_LIB + "::" + def_BTN_SELL, "BTN_SELL.bmp");
14.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp");
15.             
16.     return INIT_SUCCEEDED;
17. }
18. //+------------------------------------------------------------------+

Fragmento 02

Notem que agora estamos definindo um arquivo executável. Então toda a explicação dada acima vale para este fragmento 02. Claro que você irá precisar entender como o compilador está vendo o código. Então para ajuda, você pode olhar o código traduzido logo abaixo.

01. //+------------------------------------------------------------------+
02. int OnInit()
03. {
04.     long id;
05.     string sz;
06.     
07.     ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0);
08.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\BUY.bmp");
09.     ResourceSave("\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\SELL.bmp", "\\Files\\BTN_SELL.bmp");
10.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp");
11.             
12.     return INIT_SUCCEEDED;
13. }
14. //+------------------------------------------------------------------+

Tradução do fragmento 02 ( Código estendido )

É verdade que dificilmente você irá ver alguém digitando este código estendido. Isto por que a manutenção do código é muito mais trabalhosa. Mas para o compilador as coisas irão funcionar da mesma forma.

Pois, bem. Agora que expliquei esta parte mais simples, vamos voltar a questão dos apelidos. Quando você utiliza, apelidos aqui no MQL5. Você "força" o compilador a agir de uma forma um pouco diferente. O que irá acontecer, é que ele irá usar um sistema de compressão de dados, a fim de reduzir as coisas. Ao mesmo tempo, ele irá ignorar qualquer tipo de uso direto do recurso. Ou seja, você NÃO poderá usar diretamente a definição. É muito importante você compreender isto. Você pode adicionar qualquer coisa, no executável. Mas NÃO poderá usar diretamente o que foi adicionado.

Para de fato, poder usar a coisa que foi embutida no executável, usando um apelido. Você deverá usar o apelido e não o recurso. Ficou confuso ? Calma. Vamos tentar entender isto.

01. #resource "\\Images\\euro.bmp" as bitmap euro[][] 
02. #resource "\\Images\\dollar.bmp" 
03. //+------------------------------------------------------------------+ 
04. void Image(string name,string rc,int x,int y) 
05. { 
06.     ObjectCreate(0, name, OBJ_BITMAP_LABEL, 0, 0, 0); 
07.     ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x); 
08.     ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); 
09.     ObjectSetString(0, name, OBJPROP_BMPFILE, rc); 
10. } 
11. //+------------------------------------------------------------------+ 
12. void OnStart() 
13. { 
14.     for(int x = 0; x < ArrayRange(euro, 1); x++) 
15.             euro[ArrayRange(euro, 1) / 2][x] = 0xFFFF0000; 
16.     ResourceCreate("euro_icon", euro, ArrayRange(euro, 1), ArrayRange(euro, 0), 0, 0, ArrayRange(euro, 1), COLOR_FORMAT_ARGB_NORMALIZE); 
17.     Image("Euro" , "::euro_icon", 10, 40); 
18.     Image("USD", "::Images\\dollar.bmp", 15 + ArrayRange(euro,1), 40); 
19.     Image("E2", "::Images\\euro.bmp", 20 + ArrayRange(euro, 1) * 2, 40);
20. }
21. //+------------------------------------------------------------------+ 

Código fonte presente na documentação

Este código fonte acima, mostra exatamente isto. Esta é uma das maneiras de se usar recurso quando fazemos uso de apelidos. O apelido foi definido na linha 1. A partir daquele momento, não devemos mais usar o nome do recurso. Devemos usar o seu apelido. Por conta disto que ao executar a linha 19, teremos um erro. Pois estamos tentando usar o nome do recurso, quando na verdade devíamos usar o seu apelido.

No caso deste código mostrado acima. O apelido nos diz qual é a estrutura a ser utilizada na modelagem dos dados. Poderia ser qualquer uma entre as possíveis. Assim como também poderíamos usar meios de modo a transformar um tipo de dado em outro. Desta maneira, é possível fazer muito mais coisas, do que normalmente muitos imaginam ser possível. A diferença entre este código, retirado de forma emprestada da documentação, e o que estou utilizando, é justamente a natureza da informação.

Neste código, da documentação, estamos fazendo uso de um sistema a fim de exemplificar o uso de apelidos. Mas neste caso não é preciso salvar os dados presentes como recursos do executável. O motivo é que se trata de dados dos quais podemos fazer uso diretamente. Mas e se o recurso for um template ? Bem, pelas regras de uso. O MetaTrader 5, não nos permite utilizar templates, como sendo recursos. Então é preciso retirar, ele, template de dentro do executável, de maneira que ele deixe de ser um recurso, e passe a ser um arquivo convencional.

Isto poderia ser conseguido, da mesma forma como foi mostrado no código do fragmento 1. Onde na linha 13, tornamos uma imagem que se encontra dentro do executável como um recurso, em um arquivo, para logo depois na linha 14, a usarmos em um objeto.

Bem, isto funciona na teoria. Na prática a coisa é um pouco mais complicada. Se você observar, irá notar que na linha 13 do código de fragmento 01, estamos utilizando o nome do recurso, e não um apelido. A forma de se trabalhar com apelidos é um pouco diferente, da forma de se trabalhar com recursos. Mas este não é de fato o nosso principal problema. O nosso principal problema é o fato de não podermos colocar templates, como sendo recursos, dentro de executáveis. Com isto voltamos a nossa atenção ao código da classe C_AdjustTemplate. Isto para conseguir compreender, como fiz para contornar estes dois problemas. O de não poder colocar templates dentro de executáveis. E o de como fazer para usar o template armazenado no executável.

Pois bem, na linha 13, da classe C_AdjustTemplate, defino o template que irei utilizar. Este é o mesmo que foi visto em artigos passados sobre o Chart Trade. Mas observem que na linha 21, onde torno a definição um recurso, não o faço de modo a usar um recurso. Mas o faço de maneira a usar um apelido. Este apelido tem como base o tipo string, ou seja, isto faz com que todo o código presente no arquivo template, seja adicionado ao executável, como se fosse uma longa cadeia de caracteres. No entanto tal cadeia é como uma grande constante. É preciso que você entenda muito bem isto. O arquivo template, será tratado pelo sistema como se fosse uma constante, não que ele de fato seja uma, mas precisamos pensar assim para que o entendimento seja correto.

Pensando que o arquivo template, é uma constante string, com o apelido de IdeRad, dentro do executável. Podemos começar a pensar em como trabalhar com ele.

A primeira coisa que precisamos entender, é que de fato não podemos jogar este template, cujo apelido é IdeRad, diretamente no objeto OBJ_CHART. Isto não é possível. Precisamos transformar, estes dados em um arquivo novamente. No entanto, a função ResourceSave, não consegue dar conta deste tipo de caso. Isto por que o template, NÃO É UM RECURSO. Mas podemos fazer algo um pouco diferente. E desta forma surge o código presente entre as linhas 45 e 50.

Atenção: Estou fazendo uso desta maneira, não por ser obrigatório fazer assim. Mas pelo motivo de não desejar mudar o código, já funcional da classe. Poderíamos ler diretamente o conteúdo do template, usando o apelido IdeRad, e fazer as modificações conforme vão sendo necessárias. Mas isto complicaria a lógica, já criada e testada.

Então vamos entender, o que está acontecendo entre as linhas 45 e 50. Quando o constructor é chamado, a fim de podemos manipular o template. Informamos qual é o nível da chamada, assim como o nome do arquivo a ser acessado. Então caso a chamada seja a primeira, a linha 45, irá permitir que construamos o arquivo de template. O arquivo será construído na linha 47 e fechado na linha 49. Se existissem apenas e somente estas duas linhas, o template teria um conteúdo vazio. Mas na linha 48 a mágica acontece.

Nesta linha 48, jogamos todo o conteúdo da string dentro do arquivo. E que string é esta ? A que está na variável IdeRad. Ops. Espera um pouco. Quer dizer que armazenamos o template dentro do executável. Como sendo um recurso. Para evitar problemas, damos um apelido a ele. Depois quando queremos reaver o conteúdo dele, pegamos e lançamos o conteúdo deste apelido em um arquivo ? É Isto ? Sim. É justamente isto. Agora você deve estar pensando: Mas por que ninguém nunca pensou em fazer isto ? Mostrar como fazer isto ? Bem o motivo, não sei. Talvez por que ninguém nunca tenha realmente tentado. Ou conseguia imaginar como fazer tal coisa.

Uma vez que o arquivo seja fechado, na linha 49. Todo o restante do processo, se dá de forma idêntica ao que já foi explicado antes. Isto por que, agora o MetaTrader 5, não estará mais lidando com um recurso, mas sim com um arquivo presente no disco.

Existe um último detalhe, que não posso deixar passar em branco. Isto por que, pode ser que muitos irão ficar imaginando coisas. Ou mesmo tentando fazer algum tipo de manipulação dos dados a fim de entender o que está acontecendo, ao interagir com o indicador Chart Trade. A questão é os botões. Se você lançar o indicador no gráfico, e ele estará presente no anexo. Irá ficar sem entender, como os botões estão sendo acessados. Isto caso você tenha pouca experiência em MQL5. Você não irá em local algum, encontrar as imagens que estão sendo declaradas nos botões. Se olhar o conteúdo do template, você irá ver algo do tipo:

bmpfile_on=\Indicators\Replay\Chart Trade.ex5::Images\Market Replay\Chart Trade\BUY.bmp

bmpfile_off=\Indicators\Replay\Chart Trade.ex5::Images\Market Replay\Chart Trade\BUY.bmp

E isto não faz sentido algum. Não se você está iniciando seus estudos em MQL5. Mas se você jogar este mesmo template, no gráfico irá notar que ele é apresentado de maneira correta. Mas como ? Onde estão as imagens que ele está referenciando ? Esta é a questão. As imagens estão dentro do executável. Ou seja, no template estamos indicado diretamente a imagem que deverá ser utilizada. Assim como era no código visto, na tradução do fragmento 02. O código em questão é justamente a linha 08. Para facilitar estou mostrando ela novamente logo abaixo:

08.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\BUY.bmp");

Notem como as strings se parecem, tanto no código, quanto no template. Isto mostra o quanto o MetaTrader 5, assim como a linguagem MQL5, tem sido sub exploradas. Acreditem, o MetaTrader 5 e o MQL5, podem fazer muito mais do que você pode imaginar. Mesmo com as limitações que muitos dizem que ela tem, podemos fazer muito, mas muito mais do que parece. Sem recorrer a outras linguagens.


Conclusão

Neste artigo mostrei como fazer algo que muitos não acreditam ser possível. Usar templates dentro de um executável. Apesar de mostrar isto, de uma maneira a ser fácil de ser compreendida, este conhecimento permite você fazer muito mais coisas. É verdade que podemos, usando DLL, tornar este mesmo trabalho mostrado aqui em algo muito mais extenso.

Tudo depende da criatividade, capacidade e personalidade do programador. Tem gente que diz ser impossível fazer algo. Outros dizem não existir meios. Mas tem gente que pega e faz. Não seja, um dos que desistem ao ver o desafio. Seja um dos que resolve o problema. Meu lema é:

Um verdadeiro programador profissional : Resolve o problema. Enquanto os outros só percebem o problema.


Arquivos anexados |
Indicators.zip (149.64 KB)
Desenvolvimento de um Cliente MQTT para o MetaTrader 5: Metodologia TDD (Parte 4) Desenvolvimento de um Cliente MQTT para o MetaTrader 5: Metodologia TDD (Parte 4)
Este artigo é a quarta parte de uma série que descreve as etapas do desenvolvimento de um cliente MQL5 nativo para o protocolo MQTT. Nesta parte, examinamos as propriedades do MQTT v5.0, sua semântica, como lemos algumas delas e também fornecemos um breve exemplo de como as propriedades podem ser usadas para expandir o protocolo.
Desenvolvendo um agente de Aprendizado por Reforço em MQL5 com Integração RestAPI (Parte 5): Escolhendo o Algoritmo do agente Desenvolvendo um agente de Aprendizado por Reforço em MQL5 com Integração RestAPI (Parte 5): Escolhendo o Algoritmo do agente
Este capítulo da série aborda algoritmos de aprendizado por reforço, focando em Q-Learning, Deep Q-Network (DQN), e Proximal Policy Optimization (PPO). Explora como essas técnicas podem ser integradas para melhorar a automação de tarefas, detalhando suas características, vantagens, e aplicabilidades práticas. A seleção do algoritmo mais adequado é vista como crucial para otimizar a eficiência operacional em ambientes dinâmicos e incertos, prometendo discussões futuras sobre a implementação prática e teórica desses métodos.
Algoritmos de otimização populacional: Busca em sistema carregado (Charged System Search, CSS) Algoritmos de otimização populacional: Busca em sistema carregado (Charged System Search, CSS)
Neste artigo, vamos explorar outro algoritmo de otimização inspirado pela natureza inanimada, a busca em sistema carregado (CSS). O objetivo deste artigo é apresentar um novo algoritmo de otimização baseado nos princípios da física e mecânica.
Fatorando Matrizes — Uma modelagem mais prática Fatorando Matrizes — Uma modelagem mais prática
Muito provavelmente você não tenha se dado conta, que a modelagem das matrizes estava um tanto quanto estranha. Já que não havia a indicação de linhas e colunas, mas apenas indicações de colunas. O que é muito estranho, quando se está lendo um código, que faz fatorações de matrizes. E se você estava esperando ver linhas e colunas sendo indicadas. Pode acabar ficando bastante confuso, no momento de tentar implementar a fatoração. Além do mais, aquela forma de modelar as matrizes, não é nem de longe a melhor maneira. Isto por que, quando modelamos matrizes daquela maneira, passamos a ter uma certa limitação, que nos obriga a usar outras técnicas, ou funções, que não seriam de fato necessárias. Isto quando a modelagem é feita de uma maneira um pouco mais adequada.