English Русский 中文 Español Deutsch 日本語
preview
Letreiro de Cotação — Versão Melhorada

Letreiro de Cotação — Versão Melhorada

MetaTrader 5Indicadores | 5 outubro 2022, 11:12
856 17
Daniel Jose
Daniel Jose

Introdução

No artigo Letreiro de cotação - Versão Básica, mostrei como você pode criar um indicador, que funciona como aqueles letreiro, onde podemos ver as cotações dos ativos em tempo real. Mas você pode ter notado que lá, não implementei totalmente o indicador, não que não fosse possível faze-lo, mas por conta que ali, naquele artigo especificamente, havia a intenção de mostrar como seria o processo real de criar o indicador, e conseguir fazer com que usando um mínimo de código, você conseguisse fazer, com que o letreiro funciona-se de forma adequada, ou seja ele começasse a dar a impressão que estava girando.

Mostrei que não seria preciso você criar um código, ou melhor dizendo, um calculo para que o letreiro fosse da direita para a esquerda, e um outro calculo, para que ele fosse da esquerda para a direita, a única coisa que de fato seria preciso, seria o usuário dizer, se o valor seria incrementado ou decrementado. Então usando um único calculo, conseguiríamos fazer o indicador ir para a direita, para a esquerda ou simplesmente ficar parado ali, somente mostrando os dados do mercado para nos.

Mas aqueles dados não são tudo, ou melhor dizendo, não representam o que muitos de fato querem ver em um letreiro. Muitos querem ter uma informação mais completa e detalhada, e aqui iremos fazer isto, mas não somente isto, vamos implementar algumas coisas a mais no letreiro, de forma que ele fique mais útil, pois ter um letreiro, apenas como uma grade de cotações, ao meu ver é muito pouco, isto para não dizer, que apenas ter este comportamento, é completamente inútil, já que temos outras formas, bem mais adequadas, de montar uma grade de cotações.

Então a primeira coisa que iremos fazer, é modificar o letreiro de forma a acrescentar uma imagem, seja ela o logotipo do ativo, ou uma outra imagem qualquer, apenas para facilitar uma rápida identificação, de qual ativo estamos vendo. Dizem que uma imagem vale mais que mil palavras, então vamos ver se isto é verdade.


Implementando um novo sistema de letreiro

A primeira coisa que de fato faremos, nesta nova implementação, é criar uma nova classe objeto, de forma a fazer a abstração do objeto, que irá representar a imagem, ou logomarca do ativo, que estamos querendo observar, para isto, vamos utilizar o seguinte código:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Object_Base.mqh"
//+------------------------------------------------------------------+
class C_Object_BtnBitMap : public C_Object_Base
{
        public  :
//+------------------------------------------------------------------+
                void Create(string szObjectName, string szResource1)
                        {
                                C_Object_Base::Create(szObjectName, OBJ_BITMAP_LABEL);
                                ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_BMPFILE, 0, "\\Images\\Widget\\Bitmaps\\" + szResource1 + ".bmp");
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_STATE, false);
                        };
//+------------------------------------------------------------------+
};

Vou dar uma rápida passado no que esta acontecendo aqui, antes de explicar o por que de fazer as coisas desta forma. Primeiro vamos criar um objeto do tipo Bitmap, para receber de fato a imagem, usamos a classe genérica de forma a facilitar a criação deste objeto. Feito isto, indicar para o objeto qual deverá ser a imagem a ser utilizada, mas prestem bastante atenção a localização delas e o nome a ser utilizado, e finalmente, indicador para o objeto que a imagem a ser mostrada, é a de index 0. Desta forma, as coisas estarão completas e o sistema terá um nível bastante alto de abstração.

Bem, agora vamos entender, o por que de se fazer as coisas desta forma, e não de uma outra forma.

Para começar, você tem que entender, que existem algumas limitações nos objetos, não no que se pode fazer, mas no como se pode fazer, e o fato de usar uma classe, de forma a criar uma abstração do sistema, me permite ocultar o que esta de fato acontecendo, para o restante do código. Pouco importa, o que a classe C_Object_Bitmap, precisa fazer, para que um objeto de imagem, seja apresentado na tela, eles não querem saber como será feito, eles apenas pedem para que a classe, apresente a imagem que eles pediram.

Agora prestem atenção os seguintes fatos, caso a imagem não exista, ou seja de um formato diferente, o restante do código, não será informado sobre isto, e nem mesmo o usuário, tudo que será feito, é a apresentação de uma caixinha vazia, indicando que algo não está correto. Então caso você deseje informar ao usuário, que algo aqui não deu certo, bastaria testa o retorno da função ObjectSetString, e se ele for false, isto significaria que a imagem não pode ser carregada, e o objeto poderia ser removido da lista de objetos, mas aqui não fiz isto. e por que ?!?!

O motivo é que para mim, não faz a mínima diferença, pois todos os ativos que forem colocados no letreiro, terão junto uma imagem, que irá representar o ativo, mas também existe um outro motivo, e este talvez seja ainda mais forte.

Se você observar com calma, irá perceber que o código desta forma, não nos permite usar um tipo de imagem diferente de um arquivo bitmap, ou sequer conseguir colocar uma imagem, que irá ter um fundo transparente, não que isto irá implicar na impossibilidade de se usar este tipo de coisa, como um fundo transparente, mas em alguns casos, você pode desejar colocar um logotipo ou logomarca do ativo, e não aquela figurinha carimbada, e nestes casos, pode acontecer da imagem apresentar informações estranhas, então você precisaria, de uma outra forma de apresentar tais imagens, que não esta que é vista no código acima.

Uma destas forma é vista no artigo: Deixando o gráfico mais interessante - Adicionando uma tela de fundo, lá mostro como usar um método diferente, de forma a se construir uma imagem diretamente via recurso, é verdade que ainda assim utilizei um arquivo bitmap, por ele ter um modelo de codificação interna mais simples, mas nada impede de você, usar por exemplo um arquivo GIF, que teria uma animação ou um outro formato, mas o principal ponto, é que você utilizando aquele método, será possível criar e utilizar uma imagem, com um fundo transparente, algo que aqui não é possível.

Por conta disto, esta abstração aqui, é tão importante, caso você queira fazer uso de um outro formato de imagem, ou mesmo usar uma imagem de fundo transparente, você não precisará fazer mudança alguma no restante do código, bastará mudar o código desta classe, de forma a implementar o que você precisa e deseja.

Por este motivo, que este código, foi feito como uma classe, mesmo ele sendo tão curto e pequeno, pois assim podemos ocultar das demais partes do letreiro, do que estará acontecendo aqui.

Além desta adição ao código, temos também uma outra mudança, esta é na classe C_Object_Edit. Vamos ver o que mudou na classe:

template < typename T >
void Create(string szObjectName, color corTxt, color corBack, T InfoValue, string szFont = "Lucida Console", int iSize = 10)
        {
                C_Object_Base::Create(szObjectName, OBJ_EDIT);
                ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_FONT, szFont);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_FONTSIZE, iSize);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_ALIGN, ALIGN_LEFT);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BGCOLOR, corBack);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BORDER_COLOR, corBack);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_READONLY, true);
                SetTextValue(szObjectName, InfoValue, corTxt);
        };

Por conta de que agora, teremos mais informações no letreiro, precisamos que a classe responsável por colocar os texto no gráfico, seja um pouco mais complexa do que a que existia originalmente. Com isto agora, teremos também um controle sobre qual a fonte e qual o tamanho desta fonte, e ao fazer isto, podemos colocar coisa bem mais diversificadas do que era possível antes. Esta maior diversidade nos leva também, a mudanças, em outra função da classe.

template < typename T >
void SetTextValue(string szObjectName, T InfoValue, color cor = clrNONE, const string szSufix = "")
        {
                color clr = (cor != clrNONE ? cor : (color)ObjectGetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR));
                string sz0;
                
                if (typename(T) == "string") sz0 = (string)InfoValue; else
                if (typename(T) == "double")
                {
                        clr = (cor != clrNONE ? cor : ((double)InfoValue < 0.0 ? def_ColorNegative : def_ColoPositive));
                        sz0 = Terminal.ViewDouble((double)InfoValue < 0.0 ? -((double)InfoValue) : (double)InfoValue) + szSufix;
                }else   sz0 = "?#?";
                ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_TEXT, sz0);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR, clr);
        };

Novamente, vejam como a abstração faz toda a diferença, aqui podemos indicar diversas coisas, bem mais do que era possível antes. Então vamos ver o que estamos de fato fazendo, neste procedimento acima:

Quando uma cor é informada, ela será utilizada, caso não tenhamos um valor informado, iremos utilizar a cor que já existe no objeto, isto ajuda bastante, pois pode ser que queremos apenas mudar o texto, e neste caso a cor que o objeto já continha, irá ser mantida. Neste ponto, verificamos se o tipo informado é uma string, isto é feito em RUN TIME, então devemos assegurar que as coisas estejam corretas.

Mas também, poderemos estar utilizando um tipo double, que é usada principalmente para indicar o preço do ativo, e caso isto seja observado em RUN TIME, iremos configurar os valores de forma adequada e com um cor também adequada. Agora temos um fato curioso, que nos permite falar um pouco mais sobre o valor, e para isto usaremos um sufixo que será informado durante a chamada. Caso as coisas não sejam entendidas, temos uma formatação também para indicar isto. Logo depois apresentamos o texto e ajustamos a cor do mesmo.

Apenas pelo simples fato de termos feito estas modificações, já nos permitirá criar uma informação, da forma como pode ser vista abaixo:


ou seja, temos uma imagem no canto esquerdo, o código do ativo pode ser visto na parte superior esquerda, ainda na parte superior, só que no lado direito, temos a variação do ativo no dia, na parte inferior, no canto esquerdo, temos uma seta que indica se o ativo esta tendo uma variação positiva ou negativa, e no canto inferior direito, temos a ultima cotação do ativo.

Mas para de fato conseguir apresentar todas estas informações, temos obviamente fazer algumas mudanças na classe C_Widget, então vamos ver como ela ficou.


A nova classe C_Widget

Assim que você abre o arquivo de cabeçalho C_Widget.mqh, irá perceber que existem algumas diferenças entre este arquivo, e o existente na versão mais básica deste sistema, então vamos entender o que foi mudado:

#include "Elements\C_Object_Edit.mqh"
#include "Elements\C_Object_BackGround.mqh"
#include "Elements\C_Object_BtnBitMap.mqh"
//+------------------------------------------------------------------+
C_Terminal Terminal;
//+------------------------------------------------------------------+
#define def_PrefixName  "WidgetPrice"
#define def_MaxWidth    160
//+------------------------------------------------------------------+
#define macro_MaxPosition (Terminal.GetWidth() >= (m_Infos.nSymbols * def_MaxWidth) ? Terminal.GetWidth() : m_Infos.nSymbols * def_MaxWidth)
#define macro_ObjectName(A, B) (def_PrefixName + (string)Terminal.GetSubWin() + CharToString(A) + "#" + B)

Agora temos a inclusão da classe C_Object_BtnBitMap.mqh, este arquivo de cabeçalho, é justamente o que dará suporte para as imagens a serem utilizadas como logotipo, mas também pode ser notado, que a largura da estrutura, também foi modificada, isto para que seja possível comportar mais informações.

Talvez a coisa que mais irá chamar a atenção, é a macro que cria o nome que será utilizado nos objetos, prestem bastante atenção a esta macro, pois ela será utilizada diversas vezes.

Feito isto, entramos na parte que teremos as declarações de todas as coisas privativas da classe objeto:

class C_Widget
{
        protected:
                enum EventCustom {Ev_RollingTo};
        private :
                enum EnumTypeObject {en_Background = 35, en_Symbol, en_Price, en_Percentual, en_Icon, en_Arrow};
                struct st00
                {
                        color CorBackGround,
                              CorSymbol;
                        int   nSymbols,
                              MaxPositionX;
                        struct st01
                        {
                              string szCode;
                        }Symbols[];
                }m_Infos;

// ... Restante do código

Apesar de parecer não ter tido mudanças aqui, elas de fato surgiram, e estão presentes, então vamos dar uma rápida passada por elas, já que depois elas irão aparecer muito, e poderão ser melhor compreendidas, nos momentos em que são utilizadas. A primeira coisa que chama a atenção, é a enumeração de tipos de objetos. Esta de fato não diz que tipo de objeto estaremos criando, mas sim qual a utilidade do objeto, mas não se preocupem, conforme o código da classe for sendo visto, esta enumeração se tornará mais simples de ser entendida.

Mas ai você pode perguntar, por que não usar definições no lugar desta enumeração ?!?! O motivo é que devemos garantir, que cada um dos objetos serão únicos, e ao usar uma enumeração garantimos isto, mas se fossemos utilizar definições, correríamos o risco de ter duas definições, com o mesmo valor, e neste caso os objetos criados, não seriam seguramente únicos. Lembre-se deste detalhe quando for programar.

Agora vamos começar a ver as mudanças que aconteceram no sistema, com relação aos procedimentos internos, vamos começar com a rotina que cria o fundo do letreiro.

void CreateBackGround(void)
        {
                C_Object_BackGround backGround;
                string sz0 = macro_ObjectName(en_Background, "");
                                
                backGround.Create(sz0, m_Infos.CorBackGround);
                backGround.Size(sz0, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH), Terminal.GetHeight());
        }

Aqui usamos a primeira das enumerações, então vale bastante apena explicar por que as enumerações estão começando com o valor 35, e não em outro valor, mas primeiro vamos terminar de ver uma ultima coisa importante aqui, quando mudamos as dimensões do gráfico, é gerado uma chamada a OnChartEvent, desta forma podemos verificar qual é a nova dimensão para poder atualizar as coisas.

Mas de certa forma, podemos evitar ficar modificando as dimensões do fundo do letreiro, já que a altura do mesmo será sempre fixo, e depois veremos onde definimos esta altura, mas então, para fixar a largura, e assim poder ignorar as mudanças na largura do letreiro, utilizamos este método, que apesar de parecer não ser tão eficaz, nos garante que o fundo sempre será grande o suficiente para nos atender.

Agora vamos ver o por que de começar as enumerações em 35, se bem que podíamos usar um valor menor, mas não qualquer valor.

Se você reparar na macro que gera o nome dos objetos, irá perceber uma coisa:

#define macro_ObjectName(A, B) (def_PrefixName + (string)Terminal.GetSubWin() + CharToString(A) + "#" + B)

O primeiro argumento da macro, será a enumeração que definimos na classe, esta enumeração, irá ser transformada em um correspondente string, ou seja de valor, iremos ter um carácter, e este será usado para tornar o nome um dado único, agora se você observar a tabela ASCII, irá notar que o primeiro valor valido é o 32, e este é justamente o valor do carácter espaço, então você poderia inicializar a enumeração com o valor 32, inicializá-la com um valor inferior, não é de todo adequado.

Mas este fato só procede pelo motivo de estarmos convertendo um valor, em um carácter, caso o valor fosse convertido em seu correspondente string, nos poderíamos inicializar a enumeração como 0, já que ai, a conversão não iria nos trazer nenhum problema, mas como estamos convertendo as coisas, o valor mínimo adequado é 32.

Agora vamos ver a rotina responsável por adicionar os objetos a serem utilizados

void AddSymbolInfo(const string szArg, const bool bRestore = false)
        {
                C_Object_Edit edit;
                C_Object_BtnBitMap bmp;
                string sz0;
                const int x = 9999;

                bmp.Create(sz0 = macro_ObjectName(en_Icon, szArg), szArg);
                bmp.PositionAxleX(sz0, x);
                bmp.PositionAxleY(sz0, 15);
                edit.Create(sz0 = macro_ObjectName(en_Symbol, szArg), m_Infos.CorSymbol, m_Infos.CorBackGround, szArg);
                edit.PositionAxleX(sz0, x);
                edit.PositionAxleY(sz0, 10);
                edit.Size(sz0, 56, 16);
                edit.Create(sz0 = macro_ObjectName(en_Percentual, szArg), m_Infos.CorSymbol, m_Infos.CorBackGround, 0.0, "Lucida Console", 8);
                edit.PositionAxleX(sz0, x);
                edit.PositionAxleY(sz0, 10);
                edit.Size(sz0, 50, 11);
                edit.Create(sz0 = macro_ObjectName(en_Arrow, szArg), m_Infos.CorSymbol, m_Infos.CorBackGround, "", "Wingdings 3", 10);
                edit.PositionAxleX(sz0, x);
                edit.PositionAxleY(sz0, 26);
                edit.Size(sz0, 20, 16);
                edit.Create(sz0 = macro_ObjectName(en_Price, szArg), 0, m_Infos.CorBackGround, 0.0);
                edit.PositionAxleX(sz0, x);
                edit.PositionAxleY(sz0, 26);
                edit.Size(sz0, 60, 16);
                if (!bRestore)
                {
                        ArrayResize(m_Infos.Symbols, m_Infos.nSymbols + 1, 10);
                        m_Infos.Symbols[m_Infos.nSymbols].szCode = szArg;
                        m_Infos.nSymbols++;
                }
        }

Aqui vocês podem notar algo que para muitos é estranho, mas que para mim, não passa de uma piada, gosto de brincar e explorar as possibilidades das coisas. E pergunto: Por que fiz isto ?!?! 

Por que declarei uma variável constante, e depois fui aplicando ela nas chamadas ?!?! E o motivo é como disse a pouco. Gosto de fazer piadas em torno das possibilidades que a linguagem, seja ela qual for, nos permite.

De fato não precisaria declarar tão constante, poderia utilizar uma definição de compilação ( #define ), ou simplesmente colocar o valor da constante em cada uma das chamadas, daria no mesmo. Mas independentemente disto, observe o restante da função e vejam que vamos adicionado os elementos de forma que seus nomes, serão sempre únicos, ou seja, não haverá dois elementos com o mesmo nome, e vamos criando eles na seguinte sequencia: BitMap, Código do ativo, Percentual de variação diária, Seta indicativa da variação e o Preço atual do ativo.

Aqui temos uma coisa a ser notada, e é algo importante: Os bitmaps, que são usados no ativo, deverão ter 32 x 32 bits, caso você utilize uma dimensão diferente, terá que mudar os valores, não aqui, mas em outro local que irei mostrar logo em seguida. Mas você deve ter em mente, que as dimensões de todos os outros objetos, deverão ser ajustadas aqui, então se desejar um letreiro maior ou menor, do que o disponibilizando no anexo, bastará ajustar os valores nestas posições. Lembrando que cada uma destas funções, estão ligadas ao objeto, que foi declarado imediatamente antes da chamada.

Agora vamos ver como os dados serão de fato apresentados

inline void UpdateSymbolInfo(int x, const string szArg)
        {
                C_Object_Edit edit;
                C_Object_BtnBitMap bmp;
                string sz0;
                double v0[], v1;
                                
                ArraySetAsSeries(v0, true);
                if (CopyClose(szArg, PERIOD_D1, 0, 2, v0) < 2) return;
                v1 = ((v0[0] - v0[1]) / v0[(v0[0] > v0[1] ? 0 : 1)]) * 100.0;
                bmp.PositionAxleX(sz0 = macro_ObjectName(en_Icon, szArg), x);
                x += (int) ObjectGetInteger(Terminal.Get_ID(), sz0, OBJPROP_XSIZE);
                edit.PositionAxleX(macro_ObjectName(en_Symbol, szArg), x + 2);
                edit.PositionAxleX(sz0 = macro_ObjectName(en_Arrow, szArg), x  + 2);
                edit.SetTextValue(sz0, CharToString(v1 >= 0 ? (uchar)230 : (uchar)232), (v1 >= 0 ? def_ColoPositive : def_ColorNegative));
                edit.SetTextValue(sz0 = macro_ObjectName(en_Percentual, szArg), v1 , clrNONE, "%");
                edit.PositionAxleX(sz0, x + 62);
                edit.SetTextValue(sz0 = macro_ObjectName(en_Price, szArg), v0[0] * (v1 >= 0 ? 1 : -1));
                edit.PositionAxleX(sz0, x + 24);
        }

Aqui vamos garantir que a serie de valores, será sempre do mais recente para o mais antigo. Uma vez que temos esta garantia, podemos capturar os valores de fechamento dos preços, mas notem que estamos fazendo a leitura no diário, já que a variação que queremos, é de fato a do diário. Um detalhe: Caso, a quantidade de dados retornados seja inferior ao de dados esperados, saímos imediatamente da função, pois se continuarmos, iremos acabar por quebrar o indicador, sendo isto informado na caixa de ferramentas do MetaTrader 5. E não queremos quebrar o indicador, apenas por conta de uma falha, que muitas vezes é temporária.

Agora atenção ao cálculo seguinte, ele é o responsável por garantir a correta informação da variação percentual do ativo, acredito que não será problema para ninguém entender este calculo, já que é um calculo muito usado para dizer qual é o percentual de alta, ou de baixa em um preço.

Agora vamos posicionar as coisas na tela, e a primeira coisa a ser feita é colocar a imagem. Agora lembram que no momento da criação dos objetos, foi dito, que o tamanho da imagem, deveria ser informada em outro local ? Pois bem, o local é este este daqui, se bem que a única informação que precisamos, é a largura da imagem, mas como disse, é preferível utilizar 32 x 32, já que uma mudança, principalmente para mais, irá implicar que você precisará ajustar outros valores.

Mas caso a diferença seja para baixo, não se terá problemas, já que o próprio código, irá se ajustar a um tamanho menor de imagem, mas para um tamanho maior, principalmente na largura, poderá implicar, caso você não ajuste a dimensão informada em def_MaxWidth, fazer com que no momento que a informação for girar, ela o faça de uma maneira estranha. Bastará então aumentar o valor na definição de compilação. Mas atenção: Se você colocar um valor muito alto, terá que esperar, muito mais tempo, até a informação voltar a aparecer, do que seria preciso, caso o valor fosse adequado.

Todos os próximos valores, irão depender deste valor contido na largura da imagem, então é ela que definirá as coisas. Mas temos uma coisinha, que para alguns, não faz muito sentido, já que temos um valor, e este esta sendo convertido para carácter, e o carácter, não aparece na tela da forma como você pensava. O que esta acontecendo neste ponto ?!?! Aqui estamos criando as setas, que indicam se o preço estará caindo ou subindo, isto usando a variação do diário. A coisa em si, parece confusa, mas ela funciona super bem, lembrando que não estamos usando qualquer fonte, e sim a Wingdings 3, você pode usar uma outra fonte, mas ai terá que ajustar estes valores aqui, para que a representação se mantenha correta.

Da mesma forma, temos um outro ponto também bastante curioso. Você deve notar que estamos enviado valores do tipo string, e do tipo double, para dentro de uma mesma função, então seria de se esperar, que você encontraria duas funções diferentes, porém ao observar o código do objeto, você verá apenas uma função, isto se deve ao fato de ela estar sobrecarregada, mas a sobrecarga é criada pelo próprio compilador e linkeditor, por isto tivemos que fazer os testes dentro da função, mas se você reparar com mais atenção, verá que temos um carácter muito usado para designar valores em percentual ( % ), agora vá na função dentro do objeto Edit, e veja onde este carácter de percentual é adicionado, e como fiz isto.

E para terminar este procedimento, temos um ponto onde ajustamos o preço para que a cor dele seja corretamente mostrada no letreiro.

Observaram que as mudanças todas aconteceram dentro do próprio procedimento, não tivemos nenhuma modificação frente a forma como o sistema deveria ser de fato chamado, assim fechamos todas a modificações necessárias dentro do classe C_Widget. Mas temos umas pequenas mudanças no código do indicador, então vamos ver estas mudanças.

#property indicator_separate_window
#property indicator_plots 0
#property indicator_height 32
//+------------------------------------------------------------------+
#include <Widget\Rolling Price\C_Widget.mqh>
//+------------------------------------------------------------------+
input string user00 = "Config.cfg";  //Arquivo de configuração
input int    user01 = -1;            //Deslocamento
input int    user02 = 10;            //Pausa em milissegundos
input
 color  user03 = clrWhiteSmoke; //Cor do Ativo
input color  user04 = clrBlack;      //Cor de Fundo
//+------------------------------------------------------------------+
C_Widget Widget;
//+------------------------------------------------------------------+
int OnInit()
{
        if (!Widget.Initilize(user00, "Widget Price", user03, user04))
                return INIT_FAILED;
        EventSetMillisecondTimer(user02);
        
        return INIT_SUCCEEDED;
}

O único ponto que realmente merece ser mencionado no código do indicador, é este em que definimos um valor para a altura do indicador, não existindo nenhum outro ponto diferente em relação ao código da versão básica.

Com isto posso dizer que o indicador esta completo, mas que tal adicionarmos alguma funcionalidade extra, e que seja de alguma forma útil, para se usada durante o período de negociação ?!?! Pois este será o tema do próximo tópico.


Adicionando uma funcionalidade extra

Que tal você colocar no letreiro, ativos dos quais você gosta de operar ?!?! E não somente isto, vamos dar um passo além deste ponto. Agora sim.

Que tal ao clicar em um ativo presente no letreiro, o gráfico deste ativo, seja aberto imediatamente, de forma que você possa acompanhar as coisas, e entrar em um operação assim que notar que o ativo esta entrando em uma possível tendência, seja de baixa ou alta. E isto de forma super simples e tranquila, sem precisar digitar absolutamente nada ?!?!

Gostou da ideia, não é mesmo. Pois bem, você deve estar pensando que isto é algo ultra super mega difícil de ser feito. Que é preciso ser um programador formado, com experiência e conhecimento em FISICA NUCLEAR, ou alguém com conhecimento sobre tecnologia alienígena. Mas não. É algo super simples e direto de ser feito, usando o MQL5 juntamente com a plataforma MetaTrader 5.

Para de fato fazer isto, bastará adicionarmos um pequeno trecho de código ao sistema de tratamento de mensagens, então vamos ver o que deverá ser adicionado para que a coisa funcione.

void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
        {
                static int tx = 0;
                string szRet[];
                                                        
                switch (id)
                {

// ... Código interno ...

                        case CHARTEVENT_OBJECT_CLICK:
                                if (StringSplit(sparam, '#', szRet) == 2)
                                {
                                        SymbolSelect(szRet[1], true);
                                        szRet[0] = ChartSymbol(Terminal.Get_ID());
                                        if (ChartSetSymbolPeriod(Terminal.Get_ID(), szRet[1], PERIOD_CURRENT)) SymbolSelect(szRet[0], false);
                                        else SymbolSelect(szRet[1], false);
                                }
                                break;
                }
        }

Quando criamos os objetos, não os criamos de qualquer forma, criamos eles seguindo uma formatação bem especifica, e por conta disto, conseguimos analisar as coisa de forma a saber, qual foi o objeto clicado e por consequência, a qual ativo ele estava ligado. E é justamente neste ponto o nosso real interesse, saber a qual ativo, o objeto estava ligado. Para saber isto, utilizamos uma função bem interessante, diga-se por passagem. 

StringSplit consegue separar as coisas dependendo de como a formatação dos dados tenha sido feita, então se tudo estiver conforme o esperado, podemos ter como resultado a indicação de qual ativo, o objeto clicado estava de fato ligado, e por conta disto, poderemos falar para a plataforma MetaTrader 5, carregar o gráfico do ativo para nos.

Mas para que isto funcione, o ativo precisa estar na observação de mercado, então executamos esta linha, de forma que ele apareça na janela de observação de mercado. Mas antes de remover o ativo atual da janela, capturamos o seu código, e tentamos fazer com que o ativo clicado, apareça no gráfico, caso tenhamos sucesso, tentamos remover o ativo que estava no gráfico, da janela de observação de mercado, mas pode ser que isto não aconteça de fato, mas se ocorre uma falha, na tentativa de trocar os ativo, o ativo que seria colocado no gráfico, irá ser removido da janela de observação de mercado.

Vale lembrar alguns detalhe sobre esta troca de ativos via letreiro. A primeira coisa a ser de fato observada, é que o ativo a ser aberto, será aberto no mesmo tempo gráfico do ativo anterior, você pode mudar o tempo gráfico depois, mas a principio, ele será aberto, no mesmo tempo gráfico. Uma outra coisa igualmente importante, é que você deve selecionar os ativos, de forma que o letreiro, não tenha uma quantidade muito grande ativos, isto por conta, que eles irão demorar para voltar a ser vistos, e a cada mudança no tempo gráfico, ou troca de ativos, o letreiro irá sempre iniciar a partir do primeiro ativo na lista.

No video abaixo você pode ver como o sistema funciona na prática.



Arquivos anexados |
Últimos Comentários | Ir para discussão (17)
Daniel Jose
Daniel Jose | 5 jun 2023 em 13:14
Antonio Alberto Pera Falcao #:
Bom Dia,  Excelente Trabalho. Poderia me informar Onde consigo os "Logos" das demais empresas para poder colocar no EA. Desde Já Obrigado.

Você pode experimentar procurar no Google buscando LOGOS de empresas ...😁👍

Antonio Alberto Pera Falcao
Antonio Alberto Pera Falcao | 6 jun 2023 em 00:05
Daniel Jose #:

Você pode experimentar procurar no Google buscando LOGOS de empresas ...😁👍

Desculpa, Sou macaco velho,70 anos nas costas, jamais teria pedido sem antes experimentar o Google. Mas tudo bem, Agradeço pela Resposta.

Ney Borges
Ney Borges | 3 jan 2024 em 14:21

Para trocar os ativos alterar o arquivo Config.cfg procedimentos correto seria este ou existe outra forma automatica para o procedimentos diretamente dentro da linha de programação do mql5, tipo ler os dados do janela de ativo do mercado e colocar automaticamente, teria como fazer esse procedimentos?

obrigado por qualquer resposta sobre o assunto.

parabéns pela boa vontade e por esta trabalho de difusão do mql5 e suas funcionalidades especiais que poucos sabem, agradecido.

Ney Borges
Ney Borges | 3 jan 2024 em 19:14

Adicione uma funcionalidade que cria um arquivo.cfg de configuração de ativos que estão na aba mercado no metatrader 5, ao adicionar no grafico 

ele vai buscar todos ativos (ativos) na aba mercado para dentro do arquivo.cfg facilitando.

abaixo o codigo:

//+------------------------------------------------------------------+
// criando um arquivo com todos nomes de ativos na janela mercado.
//+------------------------------------------------------------------+
void ativos()
  {
//---
   const   bool  inWatch=true;      // True - only symbols in MarketWatch 
   const   bool  allSymbols=false;  // False - all symbols 
   int handleFile=FileOpen("Widget\\Config.cfg",FILE_WRITE|FILE_ANSI|FILE_CSV,",");
   if(handleFile==INVALID_HANDLE)
     {
      Alert("Error opening file");
      return;
     }
//   FileWrite(handleFile,"Symbol","ISIN");
   for(int i=0;i<SymbolsTotal(inWatch);i++)
     {
      FileWrite(handleFile,SymbolName(i,inWatch));
//      FileWrite(handleFile,SymbolName(i,inWatch),
//                SymbolInfoString(SymbolName(i,inWatch),SYMBOL_ISIN),
//                SymbolInfoString(SymbolName(i,inWatch),SYMBOL_PAGE),
//                SymbolInfoString(SymbolName(i,inWatch),SYMBOL_PATH));
     }
   FileClose(handleFile);
   Alert("File created");
  }
//+------------------------------------------------------------------+
Código formatado incorrectamente editado pelo moderador.

arquivo esta anexo.

salvar na pasta indicadores e compilar

Fernando Carreiro
Fernando Carreiro | 3 jan 2024 em 19:54
@Ney Borges #: abaixo o codigo:
Código formatado incorrectamente editado pelo moderador.

arquivo esta anexo.

Por favor, utilize o botão do CÓDIGO (Alt -S) ao inserir o seu código.

Botão de código no editor

Como desenvolver um sistema de negociação baseado no indicador Williams PR Como desenvolver um sistema de negociação baseado no indicador Williams PR
Bem-vindo a este novo artigo em nossa série sobre como aprender a desenvolver um sistema de negociação com base nos indicadores técnicos mais populares da MQL5. Neste artigo, nós aprenderemos como desenvolver um sistema de negociação pelo indicador %R de Williams.
Ciência de Dados e Aprendizado de Máquina (Parte 05): Árvores de Decisão Ciência de Dados e Aprendizado de Máquina (Parte 05): Árvores de Decisão
As árvores de decisão imitam a maneira como os humanos pensam para classificar os dados. Vamos ver como construir árvores e usá-las para classificar e prever alguns dados. O principal objetivo do algoritmo de árvores de decisão é separar os dados impuros em puros ou próximos a nós.
O modelo de movimento de preços e suas principais disposições (Parte 2): Equação da evolução do campo probabilístico do preço e a ocorrência do passeio aleatório observado O modelo de movimento de preços e suas principais disposições (Parte 2): Equação da evolução do campo probabilístico do preço e a ocorrência do passeio aleatório observado
O artigo considera a equação da evolução do campo probabilístico do preço e o critério do próximo salto do preço. Ela também revela a essência dos valores dos preços nos gráficos e o mecanismo para a ocorrência de um passeio aleatório desses valores.
Letreiro de Cotação — Versão Básica Letreiro de Cotação — Versão Básica
Aqui irei mostrar como criar aquelas faixas, normalmente usadas para mostrar cotações no caso das plataformas, mas usando pura e simplesmente MQL5, nada de programação externa complicada ou cheia de frescura.