English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Troca de dados entre indicadores: é fácil!

Troca de dados entre indicadores: é fácil!

MetaTrader 5Exemplos | 30 dezembro 2013, 11:23
4 423 0
Alexey Subbotin
Alexey Subbotin

Introdução

Por que nós apreciamos os novatos, é porque eles são teimosamente indispostos a usar a pesquisa, e existem muitos tópicos como "FAQ", "Para iniciantes" ou "Aquele que faz uma pergunta da lista, vai queimar no inferno". Sua verdadeira missão é fazer perguntas, como "Como ...", "é possível ..." e muitas vezes obter respostas como: "De jeito nenhum", "é impossível".

A frase eterna "nunca diga nunca novamente" vem estimulando cientistas, engenheiros e programadores a pensar e criar algo novo bem antigo esquecido.

1. Definição do problema

Por exemplo, aqui está uma citação de um dos tópicos do fórum da comunidade MQL4 (traduzido do russo):

... existem dois indicadores (vamos chamá-los de A e B). O indicador A utiliza dados diretamente a partir do gráfico de preço como de costume, e B utiliza os dados do indicador A. Aqui está a pergunta: O que devo fazer para realizar o cálculo de B de forma dinâmica, utilizando os dados do indicador de A (que já está em anexo), em vez de usar iCustom (indicador "A", ...), ou seja, se eu mudar algumas configurações do indicador A, deve ser refletido nas alterações de parâmetros do indicador B.

ou em outras palavras:

... Supondo que anexamos uma média móvel a um gráfico. Agora como conseguir o acesso direto ao seu buffer de dados?

e isto:

... Se eu chamar um indicador usando iCustom, recarrega mesmo que tenha sido carregado antes da chamada. Existe alguma maneira de evitar isso?

A lista de tais questões pode ser estendida, elas são questionadas de novo e de novo, e não apenas por novatos. Geralmente, o problema é que MetaTrader não tem nenhuma técnica de acesso aos dados indicadores personalizados sem o uso de iCustom (MQL4) ou o uso com vinculação de iCustom + CopyBuffer (MQL5). Mas seria tão tentador para o próximo desenvolvimento da obra prima escrever algumas funções no código MQL, que irá receber os dados ou calcular algo usando dados de um gráfico especificado.

Não é conveniente trabalhar com as funções padrão acima mencionadas: por exemplo, em MQL4, as cópias do indicador são criadas para cada chamador quando o iCustom é chamado, em MQL5 o problema foi parcialmente resolvido usando os punhos e, agora, os cálculos são realizado apenas uma vez para obter uma cópia mestre. Ainda não é uma panaceia: se o indicador, a que nos referimos, consome uma grande quantidade de recursos computacionais, a inanição terminal está praticamente garantida por uma dúzia de segundos em cada reinicialização.

Lembro-me de várias maneiras oferecidas para o acesso à origem, incluindo os seguintes:

  • Mapeamento de arquivos em um disco físico ou memória;
  • Uso de memória compartilhada via DLL para troca de dados;
  • O uso de variáveis globais do terminal do cliente para troca de dados e seu armazenamento.

E alguns outros métodos, que são variações dos mencionados acima, bem como algumas formas exóticas como sockets, mailslots, etc (há um método radical, que é frequentemente usado em consultoria especializada - a transferência de cálculos de indicadores diretamente ao código de consultoria especializada, mas isso é bem além do escopo do nosso artigo.

Na visão do autor, há algo em todos esses métodos, mas todos eles têm uma desvantagem comum: os dados são copiados pela primeira vez em algum local e, em seguida, distribuídos para os outros. Em primeiro lugar, ele requer alguns recursos da CPU, e em segundo lugar, cria-se um novo problema com a relevância dos dados transmitidos (não vamos considerá-lo aqui).

Então, vamos tentar definir o problema:

Queremos criar um ambiente como esse, o que proporcionaria o acesso aos dados de indicadores ligados a um gráfico, e teria as seguintes propriedades:

  • Ausência de cópia de dados (e do problema com a sua relevância);
  • Modificação mínima do código dos métodos disponíveis, se precisar usá-los;
  • Código MQL é preferível (claro, temos que usar DLL, mas vamos usar apenas uma dúzia de strings de código C++).

O autor usou C++ Builder para criação de DLLs e terminais de clientes MetaTrader 4 e MetaTrader 5. Os códigos originais apresentados a seguir são escritos em MQL5, os códigos MQL4 estão ligados ao artigo, as principais diferenças entre eles serão discutidas.

2. Matriz

Em primeiro lugar, precisamos de alguma teoria, porque vamos trabalhar com buffers indicadores, e temos de saber como seus dados estão localizados na memória. Esta informação não está documentada corretamente.

As matrizes dinâmicas em MQL são estruturas com tamanho variável, por isso é interessante como MQL resolve o problema de realocação de dados, se o tamanho da matriz aumentou e não há memória livre logo após a matriz. Há duas maneiras de resolver isso:

  1. para realocar novos dados na parte adicional de memória disponível (e salvar os endereços de todas as partes para essa matriz, por exemplo, usando uma lista de referência), ou,
  2. para mover a matriz como um todo para a nova parte da memória, que é suficiente para alocá-la.

O primeiro método leva a alguns problemas adicionais, porque neste caso, temos de investigar as estruturas de dados criadas pelo compilador MQL. A seguinte consideração ainda testemunha a segunda variante (que ainda é mais lenta): Quando uma matriz dinâmica é passada para uma função externa (para DLL), este último recebe um ponteiro para o primeiro elemento de dados, os outros elementos da matriz são ordenados e localizados logo após o primeiro elemento da matriz.

Ao passar pela referência, os dados da matriz podem mudar, então isso significa que, no primeiro método, existe um problema para copiar toda a matriz em uma área de memória separada para transferir a uma função externa, e, em seguida, adicionar o resultado à matriz original, ou seja, executar as mesmas ações que estão implícitas pelo segundo método.

Embora esta conclusão não seja 100% logicamente rigorosa, ela ainda pode ser considerada mais confiável (isso também é comprovado pela operação correta do nosso produto baseado nesta ideia).

Então, vamos supor que as seguintes afirmações são corretas:

  • Em cada momento de tempo, os dados das matrizes dinâmicas encontram-se sequencialmente na memória, um após o outro, e a matriz pode ser relocalizada em outras partes da memória do computador, mas apenas como um todo.
  • O endereço do primeiro elemento da matriz é passado para a biblioteca da DLL quando uma matriz dinâmica é passada para uma função externa como um parâmetro de referência.

E ainda tomamos algumas outras premissas: em alguns momentos, queremos dizer, nos momentos quando a função OnCalculate() (para MQL4 - que inicia a ()) do indicador correspondente é chamado, além disso, para simplificar, vamos supor que nossos buffers de dados têm a mesma dimensão e tipo de dupla [].

3. Condição necessária

MQL não suporta ponteiros (exceto para os chamados ponteiros de objetos, que não são ponteiros em seu senso comum), como tem repetidamente afirmado e confirmado os representantes da MetaQuotes Software. Então, vamos verificar se isso é verdade.

O que é um ponteiro? Não é apenas um identificador com um asterisco, ele é o endereço de uma célula em uma memória de computador. E qual é o endereço da célula? é o número de sequência iniciado a partir de um determinado princípio. E, finalmente, o que é um número? é um número inteiro que corresponde a algumas das células da memória do computador. E por que não podemos trabalhar com um ponteiro como com o número inteiro? Sim, nós podemos, porque os programas MQL trabalham perfeitamente com números inteiros!

Mas como converter um ponteiro para um inteiro? O Dynamic Link Library vai nos ajudar, vamos usar as possibilidades typecasting C++. Devido ao fato de que os ponteiros C++ são do tipo de dados de quatro bytes, no nosso caso, é conveniente usar o int como tal tipo de dados de quatro bytes.

O bit de sinal não é importante e não vamos levar isso em conta (se for igual a 1, isso significa apenas que o número inteiro é negativo), a principal coisa é que nós podemos manter todos os bits do ponteiro inalterados. Claro, podemos usar inteiros sem sinal, mas será melhor se os códigos para MQL5 e MQL4 forem semelhantes, porque MQL4 não fornece inteiro sem sinal.

Então:

extern "C" __declspec(dllexport) int __stdcall GetPtr(double *a)
{
        return((int)a);
}

Uau, agora temos a variável do tipo longa com o valor do endereço de início da matriz! Agora, precisamos aprender a ler o valor do elemento da matriz i-th:

extern "C" __declspec(dllexport) double __stdcall GetValue(int pointer,int i)
{
        return(((double*) pointer)[i]);
}

... e escrever o valor (isso não é realmente necessário no nosso caso, ainda assim ...)

extern "C" __declspec(dllexport) void __stdcall SetValue(int pointer,int i,double value)
{
        ((double*) pointer)[i]=value;
}

Então, isso é tudo. Agora podemos trabalhar com ponteiros em MQL.

4. Embalagem

Nós criamos o centro do sistema, agora temos que prepará-lo para o uso conveniente em programas MQL. No entanto, os fãs de estética, por favor não se sintam chateados, haverá algo mais.

Há um bilhão de maneiras de fazer isso. Vamos escolher o seguinte caminho. Vamos lembrar que o terminal do cliente tem uma característica especial para a troca de dados entre programas MQL separados - variáveis globais. A maneira mais natural é usá-los para armazenar ponteiros para buffers indicadores, que iremos acessar. Vamos considerar tais variáveis como uma tabela de descritores. Cada descritor terá o nome representado por uma string como:

string_identifier#buffer_number#symbol#period#buffer_length#indexing_direction#random_number,

e o seu valor será igual à representação do número inteiro do ponteiro buffer correspondente na memória do computador.

Alguns detalhes sobre os campos do descritor.

  • string_identifier - qualquer sequência (por exemplo, o nome do indicador - a variável de nome_curto pode ser utilizada, etc), que vai ser utilizada para a pesquisa do ponteiro necessário, queremos dizer, que algum indicador irá registar os descritores com o mesmo identificador e irá distingui-los usando o campo;
  • número_buffer - será usado para distinguir os buffers;
  • comprimento_buffer - precisamos dele para controlar as fronteiras, caso contrário, poderia levar a uma queda do terminal do cliente e do Windows Tela azul da morte :);
  • símbolo, período - símbolo e período para especificar a janela do gráfico;
  • direção_ordenação - especifica a direção da ordenação dos elementos da matriz: 0 – usual, 1 – reverso (AS_SERIES sinalizador é verdadeiro);
  • número_aleatório - ele será usado se houver várias cópias de um indicador, embora com diferentes janelas ou com diferentes conjuntos de parâmetros, ligados a um terminal do cliente (eles podem definir os mesmos valores do primeiro e segundo campos, é por isso que precisamos distinguir entre eles de alguma forma) - talvez não seja a melhor solução, mas funciona.

Em primeiro lugar, precisamos de algumas funções para registar o descritor e excluí-lo. Dê uma olhada na primeira string da função - a chamada da função UnregisterBuffer () é necessária para excluir descritores antigos na lista de variáveis globais.

A cada nova barreira o tamanho do buffer será aumentado em 1, por isso temos de chamar RegisterBuffer(). E se o tamanho do buffer foi alterado, o novo descritor será criado na tabela (a informação do seu tamanho está contido em seu nome), o antigo vai ficar em cima da mesa. É por isso que ele é usado.

void RegisterBuffer(double &Buffer[], string name, int mode) export
{
   UnregisterBuffer(Buffer);                    //first delete the variable just in case
   
   int direction=0;
   if(ArrayGetAsSeries(Buffer)) direction=1;    //set the right ordering_direction

   name=name+"#"+mode+"#"+Symbol()+"#"+Period()+"#"+ArraySize(Buffer)+"#"+direction;
   int ptr=GetPtr(Buffer);                      // get the buffer pointer

   if(ptr==0) return;
   
   MathSrand(ptr);                              //it's convenient to use the pointer value instead
                                                //of the current time for the random numbers base 
   while(true)
   {
      int rnd=MathRand();
      if(!GlobalVariableCheck(name+"#"+rnd))    //check for unique name - we assume that 
      {                                         //nobody will use more RAND_MAX buffers :)
         name=name+"#"+rnd;                     
         GlobalVariableSet(name,ptr);           //and write it to the global variable
         break;
      }
   }   
}
void UnregisterBuffer(double &Buffer[]) export
{
   int ptr=GetPtr(Buffer);                      //we will register by the real address of the buffer
   if(ptr==0) return;
   
   int gt=GlobalVariablesTotal();               
   int i;
   for(i=gt-1;i>=0;i--)                         //just look through all global variables
   {                                            //and delete our buffer from all places
      string name=GlobalVariableName(i);        
      if(GlobalVariableGet(name)==ptr)
         GlobalVariableDel(name);
   }      
}

Comentários detalhados são supérfluos aqui, nós apenas delineamos o fato de que a primeira função cria um novo descritor do formato acima na lista de variáveis ​globais, a segunda pesquisa através de todas as variáveis globais e exclui as variáveis e deleta descritores com o valor igual ao ponteiro.

Agora vamos considerar a segunda tarefa - a obtenção de dados do indicador. Antes de implementarmos o acesso direto aos dados, primeiro precisamos encontrar um descritor correspondente. Ele pode ser resolvido usando a seguinte função. Seu algoritmo será o seguinte: passamos por todas as variáveis ​globais e os verificamos para a presença de valores dos campos especificados no descritor.

Se nós o encontrarmos, adicionamos o seu nome à matriz, passado como o último parâmetro. Assim, como resultado, a função retorna todos os endereços de memória que as condições de pesquisa de correspondência. O valor retornado é o número de descritores encontrados.

int FindBuffers(string name, int mode, string symbol, int period, string &buffers[]) export
{
   int count=0;
   int i;
   bool found;
   string name_i;
   string descriptor[];
   int gt=GlobalVariablesTotal();

   StringTrimLeft(name);                                    //trim string from unnecessary spaces
   StringTrimRight(name);
   
   ArrayResize(buffers,count);                              //reset size to 0

   for(i=gt-1;i>=0;i--)
   {
      found=true;
      name_i=GlobalVariableName(i);
      
      StringExplode(name_i,"#",descriptor);                 //split string to fields
      
      if(StringFind(descriptor[0],name)<0&&name!=NULL) found=false; //check each field for the match
                                                                    //condition
      if(descriptor[1]!=mode&&mode>=0) found=false;
      if(descriptor[2]!=symbol&&symbol!=NULL) found=false;
      if(descriptor[3]!=period&&period>0) found=false;
      
      if(found)
      {
         count++;                                           //conditions met, add it to the list
         ArrayResize(buffers,count);
         buffers[count-1]=name_i;
      }
   }
   
   return(count);
}

Como podemos ver a partir do código de função, algumas das condições de pesquisa podem ser omitidas. Por exemplo, se passar o NULL como nome, no controle da string_identifier será omitido. O mesmo vale para outros campos: mode<0, symbol:=NULL or period<=0. Ele permite estender as opções de pesquisa na tabela de descritores.

Por exemplo, você pode encontrar todos os indicadores de média móvel em todas as janelas do gráfico, ou apenas em gráficos EURUSD com período M15 etc. Observação adicional: verificação da string_identifier é realizada pela função StringFind () em vez da verificação igualmente estrita. Ele é feito para fornecer a possibilidade de procurar por uma parte do descritor (por exemplo, quando vários indicadores definem uma string do tipo "MA (xxx)", a pesquisa pode ser realizada por substring "MA" - como resultado, encontraremos todas as médias móveis registradas).

Também usamos a função StringExplode (string s, separador de string, string & resultado []). Ele divide a string especificada para substrings, usando o separador, e escreve os resultados para a matriz do resultado.

void StringExplode(string s, string separator, string &result[])
{
   int i,pos;
   ArrayResize(result,1);
   
   pos=StringFind(s,separator); 
   if(pos<0) {result[0]=s;return;}
   
   for(i=0;;i++)
   {
      pos=StringFind(s,separator); 
      if(pos>=0)
      {
         result[i]=StringSubstr(s,0,pos);
         s=StringSubstr(s,pos+StringLen(separator));
      }
      else break;
      ArrayResize(result,ArraySize(result)+1);
   }
}

Agora, quando temos a lista de todos os descritores necessários, podemos obter os dados do indicador:

double GetIndicatorValue(string descriptor, int shift) export
{
   int ptr;
   string fields[];
   int size,direction;
   if(GlobalVariableCheck(descriptor)>0)               //check that the descriptor is valid
   {                
      ptr = GlobalVariableGet(descriptor);             //get the pointer value
      if(ptr!=0)
      {
         StringExplode(descriptor,"#",fields);         //split its name to fields
         size = fields[4];                             //we need the current array size
         direction=fields[5];                                 
         if(direction==1) shift=size-1-shift;          //if the ordering_direction is reverse
         if(shift>=0&&shift<size)                      //check for its validity - to prevent crashes
            return(GetValue(MathAbs(ptr),shift));      //ok, return the value
      }   
   } 
   return(EMPTY_VALUE);                                //overwise return empty value 
}

Como você vê, é uma embalagem da função GetValue () da DLL. É necessário verificar o descritor de validade para os limites da matriz, e para ter em conta a ordem do buffer do indicador. Se não tiver êxito, a função retorna para VALOR_VAZIO.

A alteração do buffer indicador é semelhante:

bool SetIndicatorValue(string descriptor, int shift, double value) export
{
   int ptr;
   string fields[];
   int size,direction;
   if(GlobalVariableCheck(descriptor)>0)               //check for its validity
   {                
      ptr = GlobalVariableGet(descriptor);             //get descriptor value
      if(ptr!=0)
      {
         StringExplode(descriptor,"#",fields);         //split it to fields
         size = fields[4];                             //we need its size
         direction=fields[5];                                 
         if(direction==1) shift=size-1-shift;          //the case of the inverse ordering
         if(shift>=0&&shift<size)                      //check index to prevent the crash of the client terminal
         {
            SetValue(MathAbs(ptr),shift,value);
            return(true);
         }   
      }   
   }
   return(false);
}

Se todos os valores estão corretos, ele chama a função SetValue () de DLL. O valor devolvido corresponde ao resultado da modificação: verdadeiro se bem sucedida e falsa no caso de um erro.

5. Verificando como funciona

Agora vamos tentar verificar isso. Como meta, vamos usar a Gama média verdadeira (ATR) do pacote padrão e mostrar o que temos de modificar em seu código para permitir copiar seus valores para outra janela do indicador. Também vamos testar a modificação de dados de seus dados do buffer.

O primeiro que temos que fazer é adicionar algumas linhas de código para a função OnCalculate ():

//+------------------------------------------------------------------+

//| Average True Range                                               |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &Time[],
                const double &Open[],
                const double &High[],
                const double &Low[],
                const double &Close[],
                const long &TickVolume[],
                const long &Volume[],
                const int &Spread[])
  {
   if(prev_calculated!=rates_total)
      RegisterBuffer(ExtATRBuffer,"ATR",0);
…

Como podemos ver, o novo registro de buffer deve ser feito todas as vezes, quando temos novas barras - neste caso ocorre com o passar do tempo (normalmente) ou em caso de download de dados de história adicional.

Além disso, precisamos excluir descritores da tabela após que a operação do indicador tenha acabado. é por isso que é necessário acrescentar algum código ao manipulador de evento definido (mas lembre-se que ele deve retornar e tem um parâmetro de entrada de razão interior constante):

void OnDeinit(const int reason)
{
   UnregisterBuffer(ExtATRBuffer);
}

Aqui está nosso indicador modificado na janela do gráfico do terminal do cliente (fig. 1) e o seu descritor na lista de variáveis​globais (fig. 2):

Fig. 1. Gama média verdadeira

Fig. 2. Descritor criado na lista de variáveis globais do terminal do cliente

Fig. 2. Descritor criado na lista de variáveis globais do terminal do cliente

O próximo passo é adquirir o acesso aos dados ATR. Vamos criar um novo indicador (vamos chamá-lo de teste) com o seguinte código:

//+------------------------------------------------------------------+
//|                                                         test.mq5 |
//|                                             Copyright 2009, alsu |
//|                                                 alsufx@gmail.com |
//+------------------------------------------------------------------+
#property copyright "2009, alsu"
#property link      "alsufx@gmail.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1

#include 

//---- plot ATRCopy
#property indicator_label1  "ATRCopy"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Red
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- indicator buffers
double ATRCopyBuffer[];

string atr_buffer;
string buffers[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ATRCopyBuffer,INDICATOR_DATA);
//---
   return(0);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
  {
//---
   int found=FindBuffers("ATR",0,NULL,0,buffers);   //search for descriptors in global variables
   if(found>0) atr_buffer=buffers[0];               //if we found it, save it to the atr_buffer
   else atr_buffer=NULL;
   int i;
   for(i=prev_calculated;i<rates_total;i++)
   {
      if(atr_buffer==NULL) break;
      ATRCopyBuffer[i]=GetIndicatorValue(atr_buffer,i);  //now it easy to get data
      SetIndicatorValue(atr_buffer,i,i);                 //and easy to record them
   }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Como se vê, não é difícil. Obter a lista de descritores de um substring e ler os valores do buffer indicador usando nossas funções. E finalmente, vamos escrever qualquer coisa lá (no nosso exemplo, nós escrevemos o que corresponde ao índice do elemento da matriz).

Agora executando o indicador de teste (nosso objetivo de ATR deve permanecer ligado ao gráfico). Como podemos ver na fig. 3, os valores de ATR estão na sub-janela inferior (no nosso indicador de teste), vemos a linha reta ao invés dele - na verdade, ele é preenchido com os valores correspondentes aos índices da matriz.

Fig. 3. Resultado da operação de teste.mq5

Torção do pulso - isso é tudo:)

6. Compatibilidade retroativa

O autor tentou criar uma biblioteca, escrita em MQL5, que é o máximo próximo dos padrões MQ4. Então, ele precisa de apenas algumas modificações, a fim de ser usado em MQL4.

Em particular, você deve remover a palavra-chave exportação que aparece em MQL5 e modificar o código onde temos usado o typecasting indireto (MQL4 não é tão flexível). Na verdade, o uso das funções nos indicadores é totalmente idêntica, as diferenças são apenas no método de controle de adição da história e da nova barra.

Conclusão

Algumas palavras sobre vantagens.

Parece que o uso do método descrito neste artigo não pode ser limitado a apenas a sua finalidade original. Além da construção de indicadores de cascata de alta velocidade, a biblioteca pode ser aplicada com sucesso aos consultores especializados, incluindo os seus casos de ensaio da história. Outras aplicações são difíceis de imaginar para o autor, mas, eu penso que os usuários queridos ficarão felizes em trabalhar nessa direção.

Eu acredito que os momentos técnicos a seguir podem ser necessários:

  • Melhoria da tabela de descritores (em particular, a indicação das janelas);
  • Desenvolvimento de tabela adicional para a ordenação da criação dos descritores - pode ser importante para a estabilidade dos sistemas de negociação.

Também ficarei feliz em receber quaisquer outras sugestões para melhorar a biblioteca.

Todos os arquivos necessários estão ligados ao artigo. Os códigos para MQL5, MQL4 e o código fonte da biblioteca exchng.dll estão apresentados. Os arquivos têm os mesmos locais como deveria ser nas pastas do terminal do cliente.

Ressalva

O material apresentado neste artigo descreve as técnicas de programação, que através do uso descuidado pode causar danos do software em execução no computador. A utilização dos arquivos anexados deve ser feita somente em seu próprio risco e pode levar a possíveis efeitos colaterais, embora não revelado até agora.

Reconhecimento

O autor utilizou os problemas propostos no fórum da comunidade.MQL4 http://forum.mql4.com e ideias de seus usuários: igor.senych, satop, bank, StSpirit, TheXpert, jartmailru, ForexTools, marketeer, IlyaA – desculpa se a lista está incompleta.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/19

Arquivos anexados |
cpp.zip (0.36 KB)
mql4.zip (39.29 KB)
mql5.zip (46.87 KB)
Desenhando emissões de indicador no MQL5 Desenhando emissões de indicador no MQL5
Neste artigo, consideraremos a emissão dos indicadores - uma nova abordagem para pesquisa de mercado. O cálculo da emissão é baseado na intersecção de diferentes indicadores: mais e mais pontos com diferentes cores e formas aparecem após cada tick. Eles formam vários clusters na forma de uma nebulosa, nuvens, pistas, linhas, arcos, etc. Estas formas podem ajudar a detectar as molas e forças invisíveis que afetam o movimento dos preços do mercado.
Como trocar dados: um DLL para o MQL5 em 10 minutos Como trocar dados: um DLL para o MQL5 em 10 minutos
Agora, não muitos desenvolvedores lembram como escrever um simples DLL e quais são os recursos especiais da diferente ligação do sistema. Usando vários exemplos, vou tentar mostrar todo o processo da criação de um simples DLL em 10 minutos, bem como discutir alguns detalhes técnicos da nossa implementação de ligação. Mostrarei o processo passo-a-passo da criação de DLL no Visual Studio com exemplos de troca de diferentes tipos de variáveis (números, arrys, strings, etc.). Além disso, explicarei como proteger seu terminal do cliente de travamentos nos DLLs personalizados.
Como exportar cotações do MetaTrader5 para aplicações .NET usando serviços WCF Como exportar cotações do MetaTrader5 para aplicações .NET usando serviços WCF
Quer organizar a exportação de cotas do MetaTrader 5 para sua própria aplicação? A junção MQL5-DLL permite criar essas soluções! Este artigo mostrará a você um dos meios de exportação de cotas do MetaTrader 5 para aplicações escritas no .NET. Para mim, é mais interessante, racional e fácil implementar a exportação de cotas usando esta mesma plataforma. Infelizmente, a versão 5 ainda não suporta .NET, então, como antigamente, usaremos o win32 dll com suporte .NET como intercamada.
O Histograma de preço (Perfil de mercado) e sua implementação no MQL5 O Histograma de preço (Perfil de mercado) e sua implementação no MQL5
O Perfil de mercado foi desenvolvido pelo pensador realmente brilhante Peter Steidlmayer. Ele sugeriu o uso da representação alternativa de informação sobre movimentos de mercados "horizontais" e "verticais" que levam a um conjunto de modelos completamente diferentes. Ele presumiu que existe um pulso subjacente do mercado ou de um padrão fundamental chamado de ciclo de equilíbrio e desequilíbrio. Neste artigo, considerarei o Histograma de preço - um modelo simplificado de Perfil de mercado e descreverei sua implementação no MQL5.