English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
O uso de ORDER_MAGIC para negociação com diferentes consultores especialistas em um único instrumento

O uso de ORDER_MAGIC para negociação com diferentes consultores especialistas em um único instrumento

MetaTrader 5Exemplos | 23 janeiro 2014, 14:52
4 329 0
Mykola Demko
Mykola Demko

Introdução

No MQL5 possuímos a capacidade de atribuir um número mágico para cada ordem pendente, de forma a utilizar essa informação para identificar a ordem. Isso abre as possibilidades vastas de interação entre diferentes consultores especialistas, e o desenvolvimento de sistemas ainda mais complexos. Nesse artigo, gostaria de informar o público sobre as oportunidades subestimadas do número mágico.

Mas antes de precisarmos proceder para a essência do assunto desse artigo, precisamos ganhar melhor entendimento do que constitui um número mágico. O que poderia ser mágico em um número, que determina qual consultor especialista o definiu? Os "milagres" começam com as oportunidades, que os desenvolvedores definiram no tipo ulong, que é declarado pelo número mágico.

Tipo ulong é o mais longo

Se observarmos em detalhe o tipo inteiro long, vemos que o valor máximo desse tipo é simplesmente fenomenal:

Tipo

Tamanho em bytes

Valor mínimo

Valor máximo

Análogo na linguagem C ++

long

8

-9 223 372 036 854 775 808

9 223 372 036 854 775 807

__int64

ulong

8

0

18 446 744 073 709 551 615

unsigned __int64

Tabela 1. Propriedades de tipos de dados longo e ulong

Mas o tipo ulong superou ele combinando a mantissa positiva e negativa.

Então, o comprimento especificado é enorme, mas como ele foi utilizado antes?

Em minha experiência de trabalho no mql 4, frequentemente notei a falta de sentido de codificar o número mágico por muitos desenvolvedores. O número mágico foi utilizado sensatamente, mas sua codificação parecia muito boba. O que pode ser dito sobre a individualidade do número mágico 12345, tal mágica é utilizada por quase metade da fraternidade de desenvolvimento. A segunda metade utiliza o número mágico 55555, 33333 e 77777, e isso é basicamente o conjunto completo. Quero chamar a atenção do leitor para o fato de que é improvável que o computador dele possua mais de 1.000 consultores especialistas, então o número 10000 será suficiente para codificar o nome individual de todos os consultores especialistas.

1000 - são apenas 3 categorias cheias, então o que devemos fazer com as 15 categorias cheias restantes, que estão disponíveis no tipo ulong? A resposta é: codifique elas.

O que o Wikipédia diz sobre a palavra código:

O código - regra (algoritmo), a comparação para cada mensagem individual de uma combinação estritamente particular de símbolos (caracteres) (ou sinais).

Portanto, nós faremos as regras. Proponho prescrever no código do número mágico, não apenas a identificação do consultor especialista, mas também o instrumento, no qual ele está sendo executado. O fato de que o consultor especialista está sendo executado, por exemplo, em EURUSD, não significa que o consultor especialista exibirá uma ordem apenas naquele instrumento. Também, acho que será útil escrever o código de interação do consultor especialista, algo como "seu/estrangeiro", de forma que consultor especialista, quando verificando as posições, poderia compreender que a ordem atual é construída por um consultor especialista amigável. Acho que isso será o suficiente para criar um sistema bem complexo.

E então vamos resumir o que temos: que oportunidades estamos depositando no sistema:

  1. A possibilidade de dois ou mais consultores especialistas de trabalharem em um único instrumento e não interferirem.
  2. A possibilidade de dois ou mais consultores especialistas de trabalharem em instrumentos diferentes e complementarem um ao outro.
  3. A habilidade de identificar a ordem pelo instrumento, trabalhando com o consultor especialista.

E então, a tarefa está definida, vamos começar sua implementação.

Consultor especialista simples

Faça um rascunho do código do consultor especialista simples - por exemplo, mantenha a posição na direção da móvel. Acho que o leitor, que decidiu analisar o número mágico, já leu o artigo Guia passo a passo para iniciantes para escrever um Consultor Especialista no MQL5, se não, altamente recomendo que o faça, uma vez que eu não entrarei em detalhes sobre a criação do consultor especialista. Basicamente, o consultor especialista abrirá a posição uma vez, e a utilizará para todas as outras vezes. Portanto, precisaremos da função para abrir a posição, isto é, para posicionar o pedido de negociação (ordem de negociação).

Crie uma classe auxiliária, que calculará os parâmetros para nós para preencher os campos da estrutura do pedido de negociação.

//+------------------------------------------------------------------+
//| The class provides auxiliary trading calculations                |
//+------------------------------------------------------------------+
class CProvision
  {
protected:
   MqlTradeRequest   trades;                 // pointer to the request structure of OrderSend
public:
   int               TYPE(const double &v[]);  // determines the type, in respect to the  readings of the moving
   double            pricetype(int type);     // calculates the level of the opening, in respect to the type 
   double            SLtype(int type);        // calculates the level of the stop-loss in respect to the type
   double            TPtype(int type);        // calculates the level of the take-profit, in respect to the type
   long              spread();               // returns the spread of the current instrument
   int               SendOrder(ENUM_ORDER_TYPE type,double volume);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume)
  {
   trades.action          =TRADE_ACTION_DEAL;       // Type of the implemented actions
   trades.magic           =magic;                 // Stamp of the Expert Advisor (identifier of the magic number)
   trades.symbol          =_Symbol;                // Name of the trading instrument
   trades.volume          =volume;                // Request the volume of the trade in lots
   trades.price           =pricetype((int)type);  // Price       
   trades.sl              =SLtype((int)type);     // Level of Stop Loss order
   trades.tp              =TPtype((int)type);     // Level of Take Profit order         
   trades.deviation=(int)spread();                // Maximum acceptable deviation from the requested price
   trades.type=type;                              // Order type
   trades.type_filling=ORDER_FILLING_FOK;
   if(OrderSend(trades,res)){return(res.retcode);}
   return(-1);
  }
//+------------------------------------------------------------------+
//| Determines the type, in respect to the reading of the moving     |
//+------------------------------------------------------------------+
int CProvision::TYPE(const double &v[])
  {
   double t=v[0]-v[1];
   if(t==0.0)t=1.0;
   return((int)(0.5*t/fabs(t)+0.5));
  }
//+------------------------------------------------------------------+
//| Calculates the level of opening in respect to the type           |
//+------------------------------------------------------------------+
double CProvision::pricetype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.ask);
      if(type==1)return(tick.bid);
     }
   return(-1);
  }
//+------------------------------------------------------------------+
//| Calculates the level of stop-loss in respect to the type         |
//+------------------------------------------------------------------+
double CProvision::SLtype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.bid-SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
      if(type==1)return(tick.ask+SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
     }
   return(0);
  }
//+------------------------------------------------------------------+
//| Calculates the level of timeframe in respect to the type         |
//+------------------------------------------------------------------+
double CProvision::TPtype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.bid+TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
      if(type==1)return(tick.ask-TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
     }
   return(0);
  }
//+------------------------------------------------------------------+
//| Returns the spread                                               |
//+------------------------------------------------------------------+
long CProvision::spread() 
  {
   return(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD));
  }

Possuindo tal classe, podemos escrever um código para um simples consultor especialista sem problema:

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+

//--- Input parameters
input ulong              magic       =1;           // magic
input int                SL          =300;         // Stop Loss
input int                TP          =1000;        // Take Profit
input int                MA_Period   =25;         // MA period
input double             lot         =0.1;         // Volume of position
input int                MA_shift    =0;          // Shift of indicator
input ENUM_MA_METHOD     MA_smooth   =MODE_SMA;     // Smoothing type
input ENUM_APPLIED_PRICE price       =PRICE_OPEN;    // Price type 

//--- We will store the indicator's handle
int
MA_handle,     // Handle of the indicator
type_MA,       // Type that specify the direction of MA
rezult;        // The variable takes the value of the result of the OrderSend operation
double v[2];    // Buffer for receiving values of MA

MqlTradeResult   res;   // Pointer to the structure of responding by OrderSend
MqlTick         tick;  // Pointer to the structure of last market information
CProvision      prov;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication

   if(PositionSelect(_Symbol))// If there is an open position 
     {
      if(PositionGetInteger(POSITION_TYPE)!=type_MA)// Check if its time to close
        {
         Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME),
               " reverse position of type ",PositionGetInteger(POSITION_TYPE)," by ",type_MA);
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,PositionGetDouble(POSITION_VOLUME)+lot);
         // reverse the position
         if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("#",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME),
            " open position of type ",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);
      // open position 
      if(rezult!=-1)Print("#",magic," Code of operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  } 

Execute-o e certifique-se de que o consultor especialista não tenha diferenças em rendimento, mas negocie exatamente de acordo com a lógica específica, o que é precisamente o que precisamos dele.

Figura 1. O trabalho de um consultor especialista em um único instrumento

Figura 1. O trabalho de um consultor especialista em um único instrumento

Agora vamos tentar executar esse CE, mas em diferentes cronogramas de um instrumento (para os experimentos escolhemos um instrumento aleatório, que é o EURUSD: o)

Figura 2. O conflito de dois consultores especialistas no mesmo instrumento em cronogramas diferentes

Figura 2. O conflito de dois consultores especialistas no mesmo instrumento em cronogramas diferentes

Uma vez que ambos os consultores especialistas são executados em um único instrumento, e o código não especifica a divisão de posições, então, ambos os consultores especialistas estão tentando corrigir a posição de negociação, dependendo das leituras dos seus indicadores, e como consequência disso - um conflito surge. O consultor especialista que é executado no M1, está tentando virar a posição na célula, enquanto seu rival tenta pará-lo. é óbvio que precisamos um cálculo separado de posições, o que é o que faremos agora.


Posição ou posição virtual?

Já que no MetaTrader 5 os desenvolvedores mudaram as ordens para a consideração de posições, faz sentido considerar em mais detalhes as funções associadas com o registro de posições.

// Returns the number of open positions.
int     PositionsTotal();

// Returns the symbol of the open position by the number in the list of positions.
string  PositionGetSymbol(int  index);

// Selects the open position for further working with it.
bool    PositionSelect(string  symbol, uint timeout=0);

// Function returns the requested property of the open position.
double  PositionGetDouble(ENUM_POSITION_PROPERTY  property_id);

// The function returns the requested property of the open position.
long    PositionGetInteger(ENUM_POSITION_PROPERTY  property_id);

// The function returns the requested property of the open position.
string  PositionGetString(ENUM_POSITION_PROPERTY  property_id);

Identificadores das enumerações para as funções de obtenção pedidos das propriedades de posição complacentes PositionGetDouble, PositionGetInteger, e PositionGetString que são fornecidas nas tabelas 2-4.

Identificador

Descrição

Tipo

POSITION_VOLUME

Volume da posição

double

POSITION_PRICE_OPEN

Preço da posição

double

POSITION_SL

Nível do Stop Loss para a posição aberta

double

POSITION_TP

Nível do Take Profit para a posição aberta

double

POSITION_PRICE_CURRENT

Preço atual pelo símbolo

double

POSITION_COMMISSION

Comissão

double

POSITION_SWAP

Troca acumulada

double

POSITION_PROFIT

Lucro atual

double

Tabela 2. Valor de enumeração ENUM_POSITION_PROPERTY_DOUBLE

Identificador

Descrição

Tipo

POSITION_TIME

Tempo de abertura das posições

datetime

POSITION_TYPE

Tipo da posição

ENUM_POSITION_TYPE

POSITION_MAGIC

Número mágico para a posição (ver ORDER_MAGIC )

long

POSITION_IDENTIFIER

Identificação da posição - isso é um número único que é atribuído para cada posição aberta novamente e não muda através de seu ciclo de vida. A rotatividade de uma posição não muda a sua identificação.

long

Tabela 3. Valor de enumeração ENUM_POSITION_PROPERTY_INTEGER

Identificador

Descrição

Tipo

POSITION_SYMBOL

Símbolo, para o qual a posição é aberta

cadeia (string)

POSITION_COMMENT

Comentário à posição

cadeia (string)

Tabela 4. Valor de enumeração ENUM_POSITION_PROPERTY_STRING

A partir das funções, podemos ver claramente que a linguagem não contém a divisão das posições, baseado no princípio de "quem emitiu a ordem", mas a possibilidade de tais registros está disponível já que o ORDER_MAGIC, o POSITION_MAGIC e o DEAL_MAGIC são exatamente o mesmo número, e são tomadas do número mágico, indicado pelo usuário. O POSITION_MAGIC é tomado do DEAL_MAGIC, que abre a posição, e o DEAL_MAGIC por sua vez, é tomado do ORDER_MAGIC, da ordem feita.

Identificar uma ordem, transação ou posição pode ser feito sem problema, mas é impossível liderar uma posição por um número mágico em particular. Agora vamos tentar eliminar essa deficiência. Vamos criar análogos de funções integradas, mas com identificação pelo número mágico. Declare uma classe para trabalhar com a posição virtual no número mágico.

Já que temos a oportunidade de trabalhar com o OOP, vamos também declarar a nossa própria estrutura (ganhando prática adicional de escrita objetiva).

//+------------------------------------------------------------------+
//| Structure of the CPositionVirtualMagic class                     |
//+------------------------------------------------------------------+
struct SPositionVirtualMagic
  {
   double            volume; // volume of virt. position
   ENUM_POSITION_TYPE type;  // type of virt. position
  };
//+--------------------------------------------------------------------------------+
//| The class calculates the virtual position of an Expert Advisor by magic number |
//+--------------------------------------------------------------------------------+
class CPositionVirtualMagic
  {
protected:
   SPositionVirtualMagic pvm;
public:
   double               cVOLUME(){return(pvm.volume);}
   // Returns the volume of virtual position of an Expert Advisor
   ENUM_POSITION_TYPE   cTYPE(){return(pvm.type);}
   // Returns the type of virtual position of an Expert Advisor
   bool              PositionVirtualMagic(ulong Magic,
                                          string symbol,
                                          datetime CurrentTime
                                          );
   // the method of calculation virt. position returns the presence or absence of virt. position
private:
   void              prHistory_Deals(ulong &buf[],int HTD);
   // Fills the array of tickets
  };
//+-------------------------------------------------------------------------------------+
//| Method of calculation of virt. position, returns true if there is a virt. position  |
//+-------------------------------------------------------------------------------------+
bool  CPositionVirtualMagic::PositionVirtualMagic(ulong Magic,
                                                  string symbol,
                                                  datetime CurrentTime
                                                  )
  {
   int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP));
   if(DIGITS<0)DIGITS=0;
   ulong Dticket=0;
   int History_Total_Deals=-1;
   double volume=0,volume_BUY=0,volume_SELL=0;
   ulong DTicketbuf[];

   do
     {
      if(HistorySelect(0,TimeCurrent()))
        {
         History_Total_Deals=HistoryDealsTotal();
         prHistory_Deals(DTicketbuf,History_Total_Deals);
        }
      HistorySelect(0,TimeCurrent());
     }
   while(History_Total_Deals!=HistoryDealsTotal());

   for(int t=0;t<History_Total_Deals;t++)
     {
      Dticket=DTicketbuf[t];
      if(HistoryDealSelect(Dticket))
        {
         if(HistoryDealGetInteger(Dticket,DEAL_TIME)>=CurrentTime)
           {
            if(HistoryDealGetInteger(Dticket,DEAL_MAGIC)==Magic)
              {
               if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_BUY)
                 {
                  volume_BUY+=HistoryDealGetDouble(Dticket,DEAL_VOLUME);
                 }
               else
                 {
                  if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_SELL)
                    {
                     volume_SELL+=HistoryDealGetDouble(Dticket,DEAL_VOLUME);
                    }
                 }
              }
           }
        }
      else{HistorySelect(0,TimeCurrent());t--;}
      // if there is a fault, load history data and pass the step again
     }
   volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS);
   if(volume<0)pvm.type=POSITION_TYPE_SELL;
   else
     {
      if(volume>0)pvm.type=POSITION_TYPE_BUY;
     }
   pvm.volume=fabs(volume);
   if(pvm.volume==0)return(false);
   else return(true);
  }

No texto acima (onde o código da classe CProvision foi fornecido), não foi explicado de onde tudo vem e onde vai posteriormente, uma vez que o desenvolvimento do consultor especialista não é o tópico desse artigo.

Mas vamos considerar em detalhes a classe CPositionVirtualMagic.

A classe é dada a estrutura:

struct SPositionVirtualMagic

que é utilizada para aceitar os resultados de cálculos, como uma declaração global dentro da classe, graças ao pvm (variável da estrutura), essa estrutura estará disponível em todo local, em qualquer método da classe.

Em seguida seguem os dois métodos da classe:

double               cVOLUME(){return(pvm.volume);} // Returns the volume of the virtual position of the EA
ENUM_POSITION_TYPE   cTYPE()  {return(pvm.type);}   // Returns the type of the virtual position of the EA

Esses métodos são declarados como públicos e, portanto, estarão disponíveis através da chamada classe variável, em qualquer local no programa, e são projetados para saída dos valores de estrutura no local solicitado.

Isso também é a seção em que o seguinte método é declarado:

bool              PositionVirtualMagic(ulong Magic,string symbol,datetime CurrentTime);

Essa é a função principal da classe, e devemos nos focar adicionalmente em sua análise detalhada, e enquanto isso, me precipitarei e descreverei a função sob o especificador do acesso privado:

void              prHistory_Deals(ulong &buf[],int HTD);

Esse método produz um registro de ticket de transações dentro do arranjo, que é basicamente um ciclo, e poderia ser descrito na função chamada, mas eu queria reduzir o tamanho (de forma a aumentar a legibilidade do código) da função PositionVirtualMagic(), e então movi esse ciclo além dos limites da função, e demonstrei como utilizar o especificador do acesso privado.

Então vamos voltar a PositionVirtualMagic(). Essa função, em seu início, possui uma precisão de cálculo de uma linha, para a qual você precisa arredondar o valor duplo do volume da posição calculada.

int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP)); if(DIGITS<0)DIGITS=0;

Isso é necessário para fazer a operação de comparação com zero, caso contrário algum balanço nos 8º dígitos após o ponto decimal nos impedirá de igualar o valor a zero e causará um erro de execução.

O volume da posição é arredondado ao menor passo. E se o menor passo for maior do que 1 então o arredondamento é feito pela parte inteira. Em seguida está o ciclo while, mas é utilizado em uma nova forma (diferente do ciclo no mql4), uma vez que a verificação da expansão verdadeira é feita ao final do ciclo, ao invés de no início:

    do
     {
      if(HistorySelect(0,TimeCurrent()))
        {
         History_Total_Deals=HistoryDealsTotal();
         prHistory_Deals(DTicketbuf,History_Total_Deals);
        }
      HistorySelect(0,TimeCurrent());  
     }
   while(History_Total_Deals!=HistoryDealsTotal());

Tal abordagem é utilizada porque a expressão de veracidade é computada dentro do ciclo, e em seu lançamento não é ainda preparada para essa verificação.

O ciclo contém o carregamento do histórico, eu quero chamar a atenção do leitos para o fato de que isso é uma condição exigida de forma a se certificar do trabalho das funções integradas de trabalhar com o histórico.

HistorySelect(0,TimeCurrent())

Acho que deveria explicar meu sistema de escolha dos nomes variáveis.

O leitor atento deve ter notado que os nomes das classes são definidos pela letra inicial "C", isso não é exigido por sintaxe e qualquer nome pode ser atribuído, mas tal forma torna a leitura muito mais fácil. Se a letra "C" aparece antes do nome, sabermos logo que esse é o nome da classe, e se a letra é "S" - então é uma estrutura. Se a variável toma o valor de alguma função integrada, então simplesmente mudo os componentes do nome da função e obtenho o nome da variável, por exemplo dessa forma:

CurrentTime = TimeCurrent();

Simples e legível, podemos imediatamente ver o que a variável contém. Especialmente já que o MetaEditor contém a função para arrastar um pedaço do código em particular para um local especificado.

Revisando o código posteriormente, vemos que após o carregamento do histórico, segue a chamada da função:

History_Total_Deals=HistoryDealsTotal();

Com o armazenamento do número de transações na variável. Pela mesma condição, implementaremos a verificação para o ciclo. Do que precisamos para essa verificação? E porque não podemos apenas carregar o histórico e recuperar as transações dele?

O problema está no fato de que durante o trabalho dos consultores especialistas, o histórico será solicitado separadamente por cada CE e, assim, se os consultores especialistas estão sendo executados em tempos diferentes, então a profundidade do histórico será diferente. E isso quer dizer que quando um consultor especialista entrar no ciclo e carregar o histórico para seu período, então antes de alcançar o fim do ciclo, ele pode descobrir que esse histórico já foi carregado pelo pedido de outro consultor especialista, e então a verificação para autenticidade precisa ser feita.

Incidentalmente, pode não ser o melhor tipo de verificação, mas funciona. E então, vamos continuar. No ciclo chamamos o método da classe, que insere os valores do ticket das transações em um buffer (memória de armazenamento) especialmente preparado. Após chamar a função prHistory_Deals (), nós novamente produzimos o carregamento de um histórico.

É assim que a verificação é organizada se houveram ou não quaisquer mudanças no histórico de negociações, ao longo da função prHistory_Deals(). Se não houveram mudanças, então a variável de History_Total_Deals será igual à HistoryDealsTotal(), e uma saída do ciclo para um passe único ocorrerá. Se houveram mudanças, o sistema lançará um segundo ciclo e continuará a repetir até que o histórico de tickets seja carregado sem nenhum erro (e não esqueça de colocar ";" no final):

while(History_Total_Deals!=HistoryDealsTotal());

Posteriormente no ciclo for, o cálculo das posições virtuais acontece.

Se a transação passou com sucesso por uma série de filtros (o tempo da transação e o número mágico da transação), então seu volume aumenta aquela porção da posição virtual, o tipo ao qual a transação pertence.

Quero observar que registro os cálculos das posições virtuais apenas do lançamento do consultor especialista, apesar de outras opções serem possíveis.

Aqui deve-se observar o quão exatamente a posição é calculada. Pelo livro de registros, que todos temos utilizado do tempo imemorial até hoje, possuímos despesas e lucros, e a contagem do equilíbrio é mantida como a diferença entre esses valores, o mesmo esquema se aplica ao cálculo de uma posição: se você abre os lotes, 0,2 pra vender e 0,3 para comprar, isso significa que você mantém a posição de 0,1 para comprar. Tempo de abertura e a diferença nos níveis são categorias de lucro, mas a posição que você manterá é 0,1 lotes, o tipo de Compra.

é por isso que simplesmente resumimos todas as transações, feitas pelo consultor especialista em compra e separadamente, em venda, então comparamos elas e conseguimos a posição geral (na verdade, é com isso que o resto da função examinada está ocupada).

Cálculo do volume das posições:

volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS);

Reconhecimento do tipo de posição com a saída do valor na estrutura:

   if(volume<0)pvm.type=POSITION_TYPE_SELL;
   else
     {
      if(volume>0)pvm.type=POSITION_TYPE_BUY;
     }

Resultado do volume na estrutura:

pvm.volume=fabs(volume);

O resultado do valor da função: se o volume da posição é 0, então é falsa, caso contrário, se a posição existe, então é verdadeira:

   if(pvm.volume==0)return(false);
   else return(true);

Agora, possuindo a função da posição virtual, podemos facilmente preparar o código do consultor especialista, que não irá entrar em conflito com seus "vizinhos".

Para economizar espaço, fornecerei certas partes do código, que não foram fornecidas antes, ao invés do código inteiro.

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+

//--- input parameters
input ulong              magic       =1;           // magic
input int                SL          =300;         // Stop Loss
input int                TP          =1000;        // Take Profit
input int                MA_Period   =25;          // MA period
input double             lot         =0.1;         // Volume of position
input int                MA_shift    =0;          // Shift of indicator
input ENUM_MA_METHOD     MA_smooth   =MODE_SMA;     // Smoothing type
input ENUM_APPLIED_PRICE price       =PRICE_OPEN;    // Price type 
//--- we will store the indicator's handle
int MA_handle,type_MA,rezult;
double v[2];
datetime  CurrentTime;  // The variable stores the time of start of the Expert Advisor
MqlTradeResult    res;   // Pointer to the structure of responding by OrderSend
MqlTick          tick;  // Pointer to the structure of last market information
CPositionVirtualMagic cpvm;
CProvision prov;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication

   if(cpvm.PositionVirtualMagic(magic,_Symbol,CurrentTime))// If there is ab open position 
     {
      if((int)cpvm.cTYPE()!=type_MA)// Check if it is time to close
        {
         Print("#",magic,"Position by magic number has volume ",cpvm.cVOLUME(),
               " reverse position of type ",(int)cpvm.cTYPE()," by ",type_MA);
         //cpvm.cVOLUME() - volume of virtual position
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,cpvm.cVOLUME()+lot);// reverse the poistion
         if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("#",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Poistion by magic number has volume ",cpvm.cVOLUME()," open position of type ",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position 
      if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  }

Execute o consultor especialista três vezes em um único instrumento, mas com diferentes cronogramas, e também atribua diferentes números mágicos a cada vez:

Figura 3. Vamos atribuir diferentes números mágicos a dois consultores especialistas idênticos (um instrumento, diferentes cronogramas) iniciar o primeiro consultor especialista

Figura 3. Vamos atribuir diferentes números mágicos a dois consultores especialistas idênticos (um instrumento, diferentes cronogramas) iniciar o primeiro consultor especialista

Figura 4. Vamos atribuir diferentes números mágicos a dois consultores especialistas idênticos (um instrumento, diferentes cronogramas) iniciar o segundo consultor especialista

Figura 4. Vamos atribuir diferentes números mágicos a dois consultores especialistas idênticos (um instrumento, diferentes cronogramas) iniciar o segundo consultor especialista

Figura 5. O resultado de um trabalho sem conflito de consultores especialistas em um único instrumento, com vários números mágicos

Figura 5. O resultado de um trabalho sem conflito de consultores especialistas em um único instrumento, com vários números mágicos

A execução teste passou com sucesso, os consultores especialistas gentilmente concederam caminho um ao outro, e nenhuma questão conflitante parece estar presente.

O primeiro ponto das especificações técnicas foi implementado, mas há mais por vir.


Codificando a magia

Para a implementação das seguintes partes, precisaremos desenvolver uma classe de métodos, que codificará/decodificará a informação, assim como recuperar os valores das funções integradas, e transformar eles em um formato específico.

Para fazer isso, repita os termos da codificação (por assim dizer, as especificações técnicas para o desenvolvimento):

  • Os métodos precisam codificar o nome do consultor especialista (vamos chamar ele pelo nome digital).
  • O código de reconhecimento seu/estrangeiro (vamos chamar ele de código de interação).
  • Código do símbolo, no qual o consultor especialista é executado (de forma a ser capaz de determinar da transação de onde o CE está trabalhando).

Para começar, vamos selecionar o nome para a nova classe - que seja magic (um nome genérico), atribua a nossa enumeração, para tornar o código mais compreensível visualmente.

enum Emagic
  {
   ENUM_DIGITAL_NAME,    // digital name if the Expert Advisor
   ENUM_CODE_INTERACTION,// code of interaction
   ENUM_EXPERT_SYMBOL    // symbol, on which the EA is launched
  };

A enumeração funciona de forma simples: você descreve os nomes, separados por vírgulas, e o compilador atribui a eles números pela sequência.

Primeiramente, se você atribuir uma variável de um tipo diferente (isso não é aplicável aos números), então quando especificando um parâmetro para a enumeração, você receberá um erro durante a compilação, e segundo, você obtém clareza: você não simplesmente atribui 0, mas dá o comando para atribuir ENUM_DIGITAL_NAME .

Como com a criação de uma estrutura ou uma classe, escolhi m nome simples para a enumeração. Simplesmente adicionei E ao nome selecionado de forma geral, e obtive Emagic, respectivamente, a estrutura correspondente será Smagic, e a classe Cmagic.

Novamente fique atento, pois, esta configuração não é mandatória e você pode chamar a enumeração de Enumerador, a estrutura de Estruturador e a classe de Classificador. Mas isso não fornecerá uma comunalidade em nomes e ler esse tipo de código será desconfortável.

Em seguida, vamos criar uma estrutura para armazenar nossos códigos.

struct Smagic
  {
   ulong             magicnumber;      // magic in an assembled form - how it is written in the order
   int               digital_name;     // digital name
   int               code_interaction; // code of interaction
   int               expert_symbol;    // symbol, on which the Expert Advisor is launched
  };

Após isso, declare a classe Cmagic, na qual registramos todos os métodos de decodificar e codificar a mágica, incluindo os métodos do consultor especialista anterior (simplesmente declare eles na classe atual e reescreva os cabeçalhos).

class Cmagic
  {
protected:
   Smagic            mag;
   SPositionVirtualMagic pvm;
public:
// the function returns the assembled magic, assembled from the incoming data
   ulong             SetMagic_request(int digital_name=0,int code_interaction=0);

// the function obtains the assembled magic and divides it according to the assembly logic
   ulong             SetMagic_result(ulong magicnumber);    

// the function obtains the return identification and returns the requested part of the assembled magic
   ulong             GetMagic_result(Emagic enum_); 

// the function obtains the return identification and returns the textual interpretation of the request part of the assembled magic
   string            sGetMagic_result(Emagic enum_);

// returns the voulme of the virtual position of the Expert Advisor
   double            cVOLUME(){return(pvm.volume);}
   
// returns the type of the virtual position of the Expert Advisor
   ENUM_POSITION_TYPE  cTYPE(){return(pvm.type);}
                                           
// method of calculating the virtual position, returns the presence of absence of the virtual position   
   bool              PositionVirtualMagic(Emagic enum_,
                                          string symbol,
                                          datetime CurrentTime);
private:
// function divides the magic into three parts  of three charges, and returns the part to which the category points to
   int               decodeMagic_result(int category); 

// interpretor of instrument symbols into the digital code                                                      
   int               symbolexpert();     
   
// interpretor of the digital code into the prescribed text (Expert Advisors)
   string            expertcode(int code);    
                                 
// interpretor of the digital code into the prescribed text (interaction)   
   string            codeinterdescript(int code);

// interpretor of the digital code into the instrument symbol                                         
   string            symbolexpert(int code);

// cycle of recording tickets into the buffer
   void              prHistory_Deals(ulong &buf[],int HTD);    
  };   

Agora vamos desenvolver os métodos.

Primeiro método da classe:

//+------------------------------------------------------------------+
//| Function returns the assembled magic, assembled from the input data    |
//+------------------------------------------------------------------+
ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0)
  {
   if(digital_name>=1000)Print("Incorrectly specified digital name of the Expert Advisor (more than 1000)");
   if(code_interaction>=1000)Print("Incorrectly specified the code of recognizing yours-foreign (more than 1000)");
   mag.digital_name     =digital_name;
   mag.code_interaction =code_interaction;
   mag.expert_symbol    =symbolexpert();
   mag.magicnumber      =mag.digital_name*(int)pow(1000,2)+
                         mag.code_interaction*(int)pow(1000,1)+
                         mag.expert_symbol;
   return(mag.magicnumber);
  }

Esse método recebe dois valores: um nome digital do consultor especialista e o código de interação.

ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0)

E verifica sua correção imediatamente:

   if(digital_name>=1000)Print("Incorrectly specified the digital name of the Expert Advisor(more than 1000)");
   if(code_interaction>=1000)Print("Incorrectly specifies the code of recognizing yours-foreign (more than 1000)");

Mas não há represálias contra as ações do usuário, mesmo m caso de um erro, ele simplesmente continua a trabalhar.

Em seguida vem a atribuição na estrutura dos dados de entrada, que o usuário especifica, no entanto símbolo do instrumento não é especificado e é obtido a partir do método privado:

int Cmagic::symbolexpert()

Não vou fornecer seu código, porque é muito longo e é fornecido no arquivo anexado. Deixe-me dizer que esse método é basicamente apenas uma tabela, que atribui cada a símbolo da janela de "visão de mercado" um número correspondente: para EURUSD, por exemplo, é 1, etc.

Você pode certamente obter esses dados dinamicamente, escrevendo um código para ma pesquisa de quais moedas correntes estão presentes na janela de "visão do mercado", mas a solução deve primeiro corresponder a complexidade do problema, e não faz sentido lidar com chamar as janelas, assim vamos fazer da forma simples - compreender uma lista de moedas correntes e atribuir cada uma delas a um índice.

E, finalmente, a linha mais essencial de todo o método:

mag.magicnumber      =mag.digital_name*(int)pow(1000,2)+
                      mag.code_interaction*(int)pow(1000,1)+
                      mag.expert_symbol;

Reunida das partes divergentes de toda a mágica. Essa é a mágica que será atribuída à ordem do consultor especialista.

O segundo método público da classe:

//+------------------------------------------------------------------+
//| Function obtains the assembled magic                                   |
//| and divides it according to the logic of the assembly                  |
//+------------------------------------------------------------------+
ulong Cmagic::SetMagic_result(ulong magicnumber)
  {
   mag.magicnumber      =magicnumber;
   mag.expert_symbol    =decodeMagic_result(1);
   mag.code_interaction =decodeMagic_result(2);
   mag.digital_name     =decodeMagic_result(3);
   return(mag.magicnumber);
  }

Na verdade, esse método serve como uma casca, distribuindo através da estrutura os resultados de três chamadas de um único método particular. A declaração sob tal especificador é boa no fato de que eles não são mostrados na mensagem da linha de comando que surge, quando você chama uma variável da classe, criando a impressão de que todo o trabalho foi feito por uma função pública.

Mas vamos retornar às nossas funções particulares:

//+------------------------------------------------------------------+
//| Function divides the magic into three parts of three charges              |
//| and returns the part, which the category points to             |
//+------------------------------------------------------------------+
int Cmagic::decodeMagic_result(int category)
  {
   string string_value=(string)mag.magicnumber;
   int rem=(int)MathMod(StringLen(string_value),3);
   if(rem!=0)
     {
      rem=3-rem;
      string srem="0";
      if(rem==2)srem="00";
      string_value=srem+string_value;
     }
   int start_pos=StringLen(string_value)-3*category;
   string value=StringSubstr(string_value,start_pos,3);
   return((int)StringToInteger(value));
  }

Visualmente, esse método pode ser representado como uma leitura de um número de três dígitos do campo especificado, por exemplo, se possuímos a mágica 123456789, podemos representá-la como | 123 | 456 | 789 | se o campo especificado é 1 , então o resultado será 789 uma vez que os campos são numerados da direita para a esquerda.

Assim, após utilizar todos os três campos no método chamado, distribuímos para a estrutura de todos os dados adquiridos. Isso é feito através de um procedimento de trazer a mágica para um tipo minúsculo de cadeia (string):

string string_value=(string)mag.magicnumber;

Seguido pela separação de componentes de linha individual.

Em seguida siga duas funções similares, que estão em seus interruptores de essência switch (interruptor), e diferem apenas no tipo de valores de saída:

//+------------------------------------------------------------------+
//| Function obtains the identifier of the return                          |
//| and returns the requested part of the assembled magic                   |
//+------------------------------------------------------------------+
ulong Cmagic::GetMagic_result(Emagic enum_)
  {
   switch(enum_)
     {
      case ENUM_DIGITAL_NAME     : return(mag.digital_name);     break;
      case ENUM_CODE_INTERACTION : return(mag.code_interaction); break;
      case ENUM_EXPERT_SYMBOL    : return(mag.expert_symbol);    break;
      default: return(mag.magicnumber); break;
     }
  }
//+------------------------------------------------------------------------------+
//| Function obtains the identifier of the return and returns                    |
//| a textual interpretation of the requested type of the assembled magic        |
//+------------------------------------------------------------------------------+
string Cmagic::sGetMagic_result(Emagic enum_)
  {
   switch(enum_)
     {
      case ENUM_DIGITAL_NAME     : return(expertcode(mag.digital_name));            break;
      case ENUM_CODE_INTERACTION : return(codeinterdescript(mag.code_interaction)); break;
      case ENUM_EXPERT_SYMBOL    : return(symbolexpert(mag.expert_symbol));         break;
      default: return((string)mag.magicnumber); break;
     }
  }

Funções retornam a porção da mágica, que especifica o parâmetro do tipo Emagic, com o primeiro fornecendo o resultado na forma de ulong, que é utilizado em cálculos, e o segundo fornecendo os resultados do tipo de cadeia, que podem ser utilizados para visualização.

Na função GetMagic_result () tudo é organizado de forma simples, ela distribui os valores da estrutura através do interruptor das ramificações, enquanto que sGetMagic_result () é um pouco mais complicado. Cada caso de ramificação chama uma função da tabela, que transfere o valor da estrutura para a forma visual. Assim, se o valor mag.expert_symbol = 1 , então a primeira função fornecerá 1 , e a segunda EURUSD.

Eu já descrevi as vantagens das funcionalidades da tabela em codificação/decodificação da informação, então vou apenas mencionar que cada caso deve ser considerado separadamente, baseado na complexidade de implementação de um método sem tabela, e suas vantagens ao tempo necessário para escrever tabelas. Se é mais fácil escrever uma tabela de estados, então não há necessidade de complicar as coisas. Mas se a escrita da tabela tomará muito tempo, então, obviamente, a preferência deveria ser dada aos métodos processuais. Não vou fornecer as tabelas de forma a economizar espaço (elas podem ser encontradas nos arquivos anexados).

Basicamente é isso, nossa classe está desenvolvida, no entanto, ainda há quatro funções restantes que utilizamos no desenvolvimento do consultor especialista anterior.

Simplesmente as declarei como uma nova classe, especialmente considerando que elas precisam ser levemente modificadas.

Agora o método principal:

bool  Cmagic::PositionVirtualMagic(Emagic enum_,
                                   string symbol,
                                   datetime CurrentTime)

Não apenas é declarado como m método da classe Cmagic mas também possui um conjunto diferente de parâmetros.

Em vez de mágica, ele agora consegue a identificação pelo campo da mágica, cuja posição foi calculada. Além disso, mesmo que o símbolo tenha estado presente na última opção, foi apenas utilizado para obter informações sobre o acesso do lote pelo símbolo. E agora é prescrito no filtro e pode participar, em igual com outras bases, na filtração da contagem da posição.

O que isso nos proporciona? Agora podemos simultaneamente filtrar as transações, que foram abertas em um instrumento diferente, pelo mesmo consultor especialista. Fazendo isso eles não serão confundidos com outros, consultores especialistas similares, sendo executados em um instrumento diferente. Para ser honesto, é muito difícil descrever todas as diferentes formas de utilizar esse novo sistema de cálculo. E o leitor pode pessoalmente decidir para o que ele precisa de um sistema tão complicado. Eu apenas aconselho a não complicar as circunstâncias, onde você pode escrever de forma simples, e não temer tais complicações quando há uma necessidade óbvia para isso.

Bom, já que a classe foi projetada, é hora de testar ela em um novo consultor especialista:

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+
//--- input parameters
input ulong              digital_name_       =4;           // Digital name of Expert Advisor
input ulong              code_interaction_   =1;           // Code of interaction
input Emagic             _enum               =0;           // Model of magic number  
input int                SL                  =300;         // Stop Loss
input int                TP                  =1000;        // Take Profit
input int                MA_Period           =25;          // MA period
input double             lot                 =0.4;         // Volume of position
input int                MA_shift            =0;           // Shift of indicator
input ENUM_MA_METHOD     MA_smooth           =MODE_SMA;      // Smoothing type
input ENUM_APPLIED_PRICE price               =PRICE_OPEN;    // Price type 

//--- we will store the indicator's handle
int MA_handle,type_MA,rezult;
static ulong magic;
double v[2];
datetime  CurrentTime;// The variable stores the time of start of the Expert Advisor

MqlTradeResult  res;   // Pointer to the structure of responding by OrderSend
MqlTick         tick;  // Pointer to the structure of last market information
CProvision prov;
Cmagic mg;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   magic=mg.SetMagic_request(digital_name_,code_interaction_);
// Stamp of Expert Advisor (the magic number identifier) the magic variable is declared at the global scope
// used in int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume)
   CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication
   mg.SetMagic_result(magic);// put the information into the structure
   if(mg.PositionVirtualMagic(_enum,_Symbol,CurrentTime))// If three is an open position 
     {
      if((int)mg.cTYPE()!=type_MA)// Check if it is time to close
        {
         mg.SetMagic_result(magic);// put the information into the structure
         Print("#",mg.GetMagic_result(_enum),"Position by magic number has volume ",mg.cVOLUME(),
               " reverse position of type ",(int)mg.cTYPE()," by ",type_MA);
         //cpvm.cVOLUME() - volume of virtual position
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,mg.cVOLUME()+lot);// reverse position
         if(rezult!=-1)Print("№",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("№",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Position by magic number has volume  ",mg.cVOLUME()," open position of type",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position 
      if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  }

Como mencionado anteriormente, esse consultor especialista é extremamente simples e foi criado apenas para demonstrar as diferentes capacidades, execute-o três vezes em um único instrumento:

Figura 6. Instalação de três consultores especialistas, com diferentes mágicas em diferentes gráficos

Figura 6. Instalação de três consultores especialistas, com diferentes mágicas em diferentes gráficos

Figura 7. O resultado é negociação sem conflitos de três consultores especialistas com diferentes mágicas

Figura 7. O resultado é negociação sem conflitos de três consultores especialistas com diferentes mágicas

Como pode ser observado pelas imagens das mensagens dos consultores especialistas, todos os três participantes foram lançados com sucesso e não demonstraram conflitos.


Conclusão

Fornecendo a oportunidade de atribuir ordens mágicas a operações de negociação, os criadores do MQL5, facilitaram muito a vida dor escritores de consultores especialistas. Mas os desenvolvedores podem apenas fornecer a você os instrumentos - você é quem precisa obter os diamantes.

Boa sorte e até que nos encontremos novamente.

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

Arquivos anexados |
magic_exp0_en.mq5 (8.34 KB)
magic_exp1_en.mq5 (11.84 KB)
magic_exp2_en.mq5 (25.39 KB)
Uma biblioteca para construção de um gráfico pelo Google Chart API Uma biblioteca para construção de um gráfico pelo Google Chart API
A construção de vários tipos de diagramas é uma parte essencial da análise da situação de mercado e o teste de um sistema de negócio. Frequentemente, a fim de construir um diagrama de boa aparência, é necessário organizar a saída de dados em um arquivo, após o qual é usado em aplicações como MS Excel. Isso não é muito conveniente e nos tira a capacidade de atualizar os dados dinamicamente. O Google Charts API fornece meios para criar gráficos em modos online, enviando uma solicitação especial para o servidor. Neste artigo, tentamos automatizar o processo de criação de tal solicitação e obter um gráfico a partir do servidor Google.
Os princípios do cálculo econômico de indicadores Os princípios do cálculo econômico de indicadores
Chamadas para usuário e indicadores técnicos ocupam um espaço muito pequeno no código do programa dos sistemas de negócio automatizado. Geralmente, são apenas algumas linhas de código. Mas, o que geralmente acontece é que essas poucas linhas de código são as que usam a maior parte do tempo, tempo que precisa ser gasto em teste do Expert Advisor. Então, tudo que está relacionado com cálculos de dados dentro de um indicador, precisa ser considerado mais a fundo do que só ser visto de relance. Este artigo falará precisamente sobre isso.
Uma solução livre de DLL para comunicação entre os terminais MetaTrader utilizando pipes nomeados Uma solução livre de DLL para comunicação entre os terminais MetaTrader utilizando pipes nomeados
O artigo descreve como implementar a Comunicação Interprocesso entre os terminais do cliente MetaTrader 5 usando pipes nomeados. Para o uso de pipes nomeados, a classe CNamedPipes é desenvolvida. Para o teste de seu uso e medir a conexão por ele, o indicador de tick, o servidor e os scripts do cliente são apresentados. O uso de pipes nomeados é suficiente para cotas em tempo real.
Testando o desempenho do cálculo das médias móveis no MQL5 Testando o desempenho do cálculo das médias móveis no MQL5
Uma série de indicadores apareceu desde o momento da criação do primeiro indicador de média móvel. Muitos deles usam métodos de suavização similares, mas os desempenhos de diferentes algorítimos de médias móveis não foram estudados. Neste artigo, considerarei meios possíveis de uso de Média móveis no MQL5 e comparar seus desempenhos.