Métodos de controle remoto de EAs

Dmitriy Gizlyk | 18 dezembro, 2018

Sumário

Introdução

Hoje em dia, tornou-se bastante comum o uso de robôs e EAs para negociar nos mercados financeiros. A execução perfeita de algoritmos e trabalho 24 horas por dia são considerados como as principais vantagens dos EAs. Para seu uso ininterrupto, são alugadas hospedagens virtuais, o que permite que os EAs sejam usados independentemente 24 horas por dia.

Mas, infelizmente, nem todos eles funcionam igualmente bem em todas as situações de mercado. Ás vezes, é necessário gerenciar manualmente seu trabalho: habilitar e desabilitá-los. Isso é fácil de fazer quando o usuário está perto do terminal. Porém, o que se deveria fazer, se você não tiver acesso rápido a terminais com EAs em funcionamento? Nesses casos, seria bom poder controlar remotamente seu trabalho. Vamos considerar um dos possíveis métodos de controle remoto de EAs no terminal.

1. Formulação do problema

À primeira vista, a tarefa parece bastante clara: basta criar um programa que, ao receber comandos externos, dê ordens aos nossos EAs. No entanto, considerando a questão mais profundamente, entendemos que o MetaTrader 5 não possui a capacidade de usar programas para manipular o trabalho de EAs de terceiros. Cada EA trabalha numa thread separada, não se podendo determinar a presença de EAs em execução, como acontece com qualquer um dos gráficos abertos. A solução para essa questão foi sugerida pelo usuário fxsaber na biblioteca "Expert - biblioteca para o MetaTrader 5".

Nesse código, ao autor lhe foi pedido usar um recurso para salvar modelos. À primeira vista, o armazenamento de modelos não afeta a operação de programas que estão sendo executados no gráfico. Mas, quando um modelo gráfico é salvo, no arquivo são gravados todos os objetos existentes no gráfico, bem como todos os programas em execução com seus parâmetros. Ao aplicar ao gráfico o modelo salvo, é possível restaurar todos os objetos e programas com os parâmetros salvos no arquivo.

Uma segunda consequência dessa ação é a remoção completa de todos os objetos e programas existentes antes do carregamento do modelo. Em outras palavras, se um EA estiver sendo executado no gráfico e não houver EA no modelo carregado, o EA será removido do gráfico e vice-versa. Como resultado dessa solução alternativa, basta editar o arquivo do modelo, para carregar e excluir EAs dos gráficos.

Naturalmente, existe a possibilidade de preparar os modelos com antecedência e carregá-los sem a necessidade de editá-los. Mas, com essa abordagem, o número de modelos necessários cresce duas vezes mais rápido do que o número de EA usados. Além disso, se forem usadas diferentes configurações do EA para diferentes instrumentos, a adição de cada instrumento aumentará ainda mais o número de modelos. Nesse caso, preparação de modelos se transforma em trabalho rotineiro, e no futuro é necessário não ficar confuso em sua implementação.

A edição de modelos também tem suas próprias nuances. Por padrão, os modelos são salvos no diretório especial "diretório_de_dados\Profiles\Templates\". Mas a MQL5 permite que você trabalhe apenas com arquivos da área restrita (ambiente de simulação). Uma solução para esse problema também foi encontrada por fxsaber. Ele sugeriu adicionar o caminho para a área restrita ao especificar o nome do arquivo de modelo. Como resultado dessa ação, é possível acessar o arquivo de modelo sem usar bibliotecas de terceiros.

Agradecemos ao usuário fxsaber por sua mente perspicaz e por suas ideias extraordinárias.

Como já definimos os métodos de gerenciamento de EAs, agora vamos pensar no modelo de mensagens do programa com o usuário. Hoje em dia, embora as pessoas sejam obrigadas a estarem em constante movimento, afastando-se de computadores, seus smartphones sempre estão com elas. Tanto para o iPhone quanto para o Android, existem aplicativos de terminal MetaTrader 5. Ao conectar esse terminal à sua conta, o usuário pode analisar os movimentos de preços em gráficos e negociar manualmente. Mas, infelizmente, hoje os terminais móveis não permitem o uso de EAs e indicadores de terceiros. É por isso que para EAs ainda são usadas versões desktop do terminal rodando no computador do usuário ou, mais frequentemente, em hospedagens virtuais.

Embora o terminal móvel conectado à conta nos permita olhar para ela num certo contexto, não existe comunicação direta entre os terminais móveis e os desktop. A única coisa que podemos mudar é colocar e excluir ordens. As ordens colocadas são exibidas imediatamente na conta e podem ser rastreadas pelo EA em execução no terminal desktop. Assim, juntamente com a colocação de ordens, podemos transferir comandos de controle para o nosso EA mestre. Logo, resta apenas determinar a lista de comandos e com qual código passá-los. Vou me concentrar nessas questões em mais detalhes nos capítulos seguintes.

2. Analisando o arquivo do modelo

Agora que começamos o trabalho, proponho analisar a estrutura do arquivo de modelo. Abaixo está um exemplo de um modelo de gráfico EURUSD M30 com o EA ExpertMACD e o indicador MACD instalados.

<chart>
id=130874249669663027
symbol=EURUSD
period_type=0
period_size=30
digits=5
.....
.....
windows_total=2

<expert>
name=ExpertMACD
path=Experts\Advisors\ExpertMACD.ex5
expertmode=0
<inputs>
Inp_Expert_Title=ExpertMACD
Inp_Signal_MACD_PeriodFast=12
Inp_Signal_MACD_PeriodSlow=24
Inp_Signal_MACD_PeriodSignal=9
Inp_Signal_MACD_TakeProfit=50
Inp_Signal_MACD_StopLoss=20
</inputs>
</expert>

<window>
height=162.766545
objects=103

<indicator>
name=Main
........
</indicator>
<object>
.......
</object>

<object>
........
</object>
........
........
<object>
........
</object>

</window>

<window>
height=50.000000
objects=0

<indicator>
name=MACD
path=
apply=1
show_data=1
scale_inherit=0
scale_line=0
scale_line_percent=50
scale_line_value=0.000000
scale_fix_min=0
scale_fix_min_val=-0.001895
scale_fix_max=0
scale_fix_max_val=0.001374
expertmode=0
fixed_height=-1

<graph>
name=
draw=2
style=0
width=1
color=12632256
</graph>

<graph>
name=
draw=1
style=2
width=1
color=255
</graph>
fast_ema=12
slow_ema=24
macd_sma=9
</indicator>
</window>
</chart>

Como pode ser visto no código apresentado, as informações no arquivo de modelo são estruturadas e divididas por tags. O arquivo começa com a tag <chart> descrevendo informações básicas sobre o gráfico em que você pode encontrar o identificador, o instrumento e o timeframe do gráfico. As informações sobre o EA em quem estamos interessados estão entre as tags <expert> e </expert>.

No início do bloco, são apresentadas informações sobre o EA: nome abreviado exibido no gráfico e caminho para o arquivo. Em seguida, o estado da tag expertmode indica que o EA pode realizar operações de negociação. Após isso, seguem os parâmetros do EA, destacados pelas tags <inputs> </inputs>. Isto é seguido por informações sobre as subjanelas do gráfico. Cada subjanela é separada pelas tags <window> e </window>, entre elas são descritos os indicadores em execução (separados pelas tags <indicator> ... </indicator>) e são colocados os objetos gráficos (selecionados pelas tags <object> ... </object>).

Também repare que, se um EA e/ou indicador em execução cria no gráfico quaisquer objetos gráficos, eles deverão ser removidos do modelo. Caso contrário, ao anexar um modelo ao gráfico, esses objetos serão plotados no gráfico a partir do modelo e, ao iniciar esse EA e/ou indicador, eles criarão os mesmos objetos novamente.

Isso pode levar à criação descontrolada de um grande número de objetos idênticos no gráfico, bagunçando o gráfico e consumindo recursos computacionais excessivamente. Mas também pode ocorrer o pior cenário, isto é, quando o programa, por um lado, dá erro ao criar um objeto e, por outro lado, é descarregado do gráfico, levando à sua desconexão.

Os objetos criados devem ser protegidos de modificações feitas pelo usuário, para esse fim, eles são ocultos na lista de objetos gráficos, atribuindo ao objeto a propriedade de oculto. Para definir essa propriedade, o modelo possui a propriedade hidden, à qual é atribuído o valor de 1 para objetos ocultos.

Assim, para habilitar/desabilitar o EA, basta reescrever o arquivo de modelo, alterando o sinalizador expertmode para o valor que precisamos e excluindo objetos ocultos simultaneamente. A implementação de um novo modelo reinicia o EA com a propriedade que precisamos.

3. Definindo comandos

Já estabelecidos os princípios básicos do trabalho do EA, é a hora de desenvolver um sistema de mensagens. Já decidimos usar ordens para enviar comandos. Mas como enviar comandos de ordens sem estragar o resultado financeiro? Para esses fins, usaremos ordens pendentes, que serão excluídas pelo EA após receber o comando.

Ao colocar ordens pendentes, devemos ter certeza de que não será ativado no intervalo de tempo da ordem pendente. A solução desse problema está na superfície, pois precisamos fazer uma ordem a uma distância suficiente do preço atual.

Para minha surpresa, quando estava trabalhando com terminais móveis, achei impossível deixar comentários sobre ordens abertas. Além disso, o usuário pode ver os comentários para as ordens criadas no terminal desktop. Assim, podemos organizar a comunicação unidirecional para o EA mestre transferir mensagens para o usuário. Mas o usuário não pode dar comandos dessa maneira.

Podemos definir ordens pendentes que serão lidas pelo EA, mas não podemos deixar comentários nelas. Nesse caso, podemos usar o preço e o instrumento da ordem para transferir o comando. Aqui devemos determinar qual preço definir para codificar o comando, enquanto isso, ele deve estar a uma distância suficiente do preço atual para que a ordem colocada não seja acionada sob nenhuma circunstância.

Na minha opinião, esses preços podem estar próximos de zero. Teoricamente, a probabilidade de aproximar o preço de qualquer instrumento a zero é insignificante. Assim, ao definir o preço da ordem para 1 a 5 ticks, praticamente não corremos o risco de ele ser acionado. Naturalmente, nesse caso, teremos que limitar nosso stop-loss de venda, uma vez que o uso de stops não afeta a margem livre.

Não vamos esquecer que no aplicativo móvel podemos ler os comentários de ordens, ou seja, nosso EA mestre pode nos enviar informações dessa maneira.

Resumindo o mencionado, é proposta a seguinte codificação de comandos:

Preço Volume Comando
1 1 tick Qualquer um Solicitação de status do EA.
O EA mestre coloca ordens pendentes em instrumentos com EAs, o comentário da ordem mostra o nome do EA e a permissão para negociar.
2 2 ticks Qualquer um Parada da negociação do EA.
Se não houver comentários na ordens, todos os EA param. Se um comando é dado como resultado da modificação de uma ordem após o primeiro comando, um EA específico para num instrumento específico.
3 3 ticks Qualquer um Inicialização do EA de negociação.
Princípios de operação são semelhantes ao comando 2.
4 4 ticks 1 min lote BUY
2 min lote SELL
3 min lote TODOS
Remove ordens pendentes.
5 5 ticks 1 min lote BUY
2 min lote SELL
3 min lote TODOS
Fecha posições.


4. Criando o EA mestre

Agora que temos uma compreensão completa dos métodos de trabalho e do canal de comunicação, podemos passar à escrita de nosso EA imediatamente. Ele usará métodos da biblioteca "Expert - Biblioteca para o MetaTrader 5 "com uma pequena alteração, isto é, para facilitar o trabalho, tornaremos todos os métodos de biblioteca públicos.

Primeiro, atribuímos nomes mnemônicos às tags usadas. Alguns deles já foram declarados na biblioteca utilizada.

#define FILENAME (__FILE__ + ".tpl")
#define PATH "\\Files\\"
#define STRING_END "\r\n"
#define EXPERT_BEGIN ("<expert>" + STRING_END)
#define EXPERT_END ("</expert>" + STRING_END)
#define EXPERT_INPUT_BEGIN ("<inputs>" + STRING_END)
#define EXPERT_INPUT_END ("</inputs>" + STRING_END)
#define EXPERT_CHART_BEGIN ("<chart>" + STRING_END)
#define EXPERT_NAME "name="
#define EXPERT_PATH "path="
#define EXPERT_STOPLEVEL "stops_color="

Declaramos no código do nosso EA.

#define OBJECT_BEGIN             ("<object>" + STRING_END)
#define OBJECT_END               ("</object>" + STRING_END)
#define OBJECTS_NUMBER           ("objects=")
#define OBJECT_HIDDEN            ("hidden=1")
#define EXPERT_EXPERTMODE        ("expertmode=")

Observe também que fxsaber tomou conta da compatibilidade de sua biblioteca com outros códigos, eliminando os nomes mnemônicos no final da biblioteca. Essa abordagem elimina erros ao reatribuir o mesmo nome a outra macro, mas não permite o uso de tais declarações fora da biblioteca. Para não repetir declarações semelhantes de macros no código do EA, vamos fechar os comentários da diretiva #undef no código da biblioteca.

//#undef EXPERT_STOPLEVEL
//#undef EXPERT_PATH
//#undef EXPERT_NAME
//#undef EXPERT_CHART_BEGIN
//#undef EXPERT_INPUT_END
//#undef EXPERT_INPUT_BEGIN
//#undef EXPERT_END
//#undef EXPERT_BEGIN
//#undef STRING_END
//#undef PATH
//#undef FILENAME

Em seguida, vamos declarar duas variáveis externas do nosso EA mestre: o tempo de vida das ordens em minutos e o magic para sua identificação.

sinput int      Expirations =  5;
sinput ulong    Magic       =  88888;

Adicionamos ao código do nosso EA a biblioteca acima indicada e à biblioteca de operações de negociação.

#include <fxsaber\Expert.mqh>
#include <Trade\Trade.mqh>

No bloco de variáveis globais, declararemos uma instância da classe de operações de negociação e variáveis para armazenar o identificador do gráfico de trabalho e o ticket da última ordem de comando.

CTrade   *Trade;
ulong     chart;
ulong     last_command;

Na função OnInit, inicializamos as variáveis globais.

int OnInit()
  {
//---
   Trade =  new CTrade();
   if(CheckPointer(Trade)==POINTER_INVALID)
      return INIT_FAILED;
   Trade.SetDeviationInPoints(0);
   Trade.SetExpertMagicNumber(Magic);
   Trade.SetMarginMode();
   Trade.SetTypeFillingBySymbol(_Symbol);
//---
   chart=ChartID();
   last_command=0;
//---
   return(INIT_SUCCEEDED);
  }

Devemos nós lembrar de remover a instância da classe de operações de negociação na função OnDeinit.

void OnDeinit(const int reason)
  {
//---
   if(CheckPointer(Trade)!=POINTER_INVALID)
      delete Trade;
  }


4.1. Gerenciador de comandos

A inicialização do recurso principal do EA mestre será feito a partir da função OnTradeTransaction. Deve ser lembrado que o evento de chamada dessa função pode ser processado várias vezes para uma ordem. Portanto, antes de executar o código do programa, realizaremos uma série de verificações.

Verificamos a disponibilidade do ticket da ordem para o evento que está sendo processado. Verificaremos o status da ordem para não lidar com o evento de exclusão de ordens. Verificamos o magic da ordem para excluir o processamento de ordens de EAs. Verificamos o tipo de ordem, uma vez que nossos comandos chegam apenas com ordens stop para venda. Além disso, verificamos o tipo de operação de negociação, ela deve ser a adição ou alteração de ordem.

Após todas as verificações serem concluídas com sucesso, iniciamos o gerenciador de comandos, que é organizado na função CheckCommand.

void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest &request,
                        const MqlTradeResult &result)
  {
//---
   if(trans.order>0 && trans.order_state!=ORDER_STATE_REQUEST_CANCEL && request.magic==0 && 
      trans.order_type==ORDER_TYPE_SELL_STOP && 
      (trans.type==TRADE_TRANSACTION_ORDER_ADD || trans.type==TRADE_TRANSACTION_ORDER_UPDATE))
      CheckCommand(trans,request);
  }

Ao inicializar a função CheckCommand, em seus parâmetros ela recebe as estruturas da operação e da solicitação de negociação. Em primeiro lugar, verificamos se essa solicitação foi processada anteriormente. Caso o comando já tenha sido processado, saímos da função.

void CheckCommand(const MqlTradeTransaction &trans,
                  const MqlTradeRequest &request)
  {
   if(last_command==trans.order)
      return;

Se o comando ainda não foi processado, decodificamos o preço da ordem no comando.

   double tick=SymbolInfoDouble(trans.symbol,SYMBOL_TRADE_TICK_SIZE);
   uint command=(uint)NormalizeDouble(trans.price/tick,0);

Em seguida, com a ajuda do operador switch chamamos a função correspondente ao comando de entrada.

   switch(command)
     {
      case 1:
        if(StringLen(request.comment)>0 || trans.type==TRADE_TRANSACTION_ORDER_UPDATE)
           return;
        if(trans.order<=0 || (OrderSelect(trans.order) && StringLen(OrderGetString(ORDER_COMMENT))>0))
           return;
        GetExpertsInfo();
        break;
      case 2:
        ChangeExpertsMode(trans,request,false);
        break;
      case 3:
        ChangeExpertsMode(trans,request,true);
        break;
      case 4:
        DeleteOrders(trans);
        break;
      case 5:
        ClosePosition(trans);
        break;
      default:
        return;
     }

No final, salvaremos o ticket de última ordem de comando e excluiremos a ordem da conta.

   last_command=trans.order;
   Trade.OrderDelete(last_command);
  }

O código completo de todas as funções pode ser encontrado no anexo.


4.2. Informações sobre EAs em execução

A função GetExpertsInfo é responsável por obter informações sobre os Expert Advisors em execução no terminal. Como definimos anteriormente, essa função colocará ordens stop de informações, cujo instrumento exibe em qual instrumento está sendo executado o EA, e nos comentários da ordem, exibiremos o nome e o status do EA.

No início da função, excluímos as ordens colocadas anteriormente, chamando a função DeleteOrdersByMagic.

void GetExpertsInfo(void)
  {
   DeleteOrdersByMagic(Magic);

Em seguida, organizamos um ciclo para percorrer todos os gráficos ativos no terminal. Começamos o ciclo verificando se o gráfico analisado não é o gráfico de trabalho do nosso EA mestre. Se for assim, avançamos para o próximo gráfico.

   long i_chart=ChartFirst();
   while(i_chart>=0 && !IsStopped())
     {
      if(i_chart==0 || i_chart==chart)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

Na próxima etapa, carregamos o modelo do gráfico, enquanto verificamos preliminarmente se no gráfico há EAs. Se não houver EA no gráfico ou o modelo não estiver carregado, prosseguimos com o próximo gráfico.

      string temp=EXPERT::TemplateToString(i_chart,true);
      if(temp==NULL)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

Em seguida, encontramos o bloco do EA no modelo. Se o bloco não for encontrado, avançamos para o próximo gráfico.

      temp=EXPERT::StringBetween(temp,EXPERT_BEGIN,EXPERT_END);
      if(temp==NULL)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

Depois disso, extraímos o nome e o status do EA. A futura ordem de informações será formada a partir dele. Se o EA está autorizado a negociar, antes de seu nome nos comentários colocamos a letra "T", caso contrário, a letra "S".

      string name =  EXPERT::StringBetween(temp,EXPERT_NAME,STRING_END);
      bool state  =  (bool)StringToInteger(EXPERT::StringBetween(temp,EXPERT_EXPERTMODE,STRING_END));
      string comment =  (state ? "T " : "S ")+name;

No final do ciclo, definimos o instrumento do gráfico, o período de validade da ordem de informação, enviamos a ordem e prosseguimos com o próximo gráfico.

      string symbol=ChartSymbol(i_chart);
      ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC;
      datetime expir=0;
      if(Expirations>0)
        {
         expir=TimeCurrent()+Expirations*60;
         type=ORDER_TIME_SPECIFIED;
        }
      Trade.SellStop(SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN),SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE),symbol,0,0,type,expir,comment);
      i_chart=ChartNext(i_chart);
     }
  }

Na função considerada, foram utilizados os métodos da biblioteca do usuário fxsaber, para obter o modelo de gráfico numa variável de string e para obter a substring entre as tags especificadas.

Para obter o modelo numa variável de string do gráfico especificado, primeiro, se necessário, verificamos se no gráfico há EAs. Em seguida, salvamos o modelo do gráfico especificado e lemos o modelo resultante como uma matriz binária. Depois disso, convertemos a matriz numérica numa string e retornamos a função chamada. Se houver erros em qualquer um dos estágios de verificação, a função retornará o valor NULL.

  static string TemplateToString( const long Chart_ID = 0, const bool CheckExpert = false )
  {
    short Data[];
    return(((!CheckExpert || EXPERT::Is(Chart_ID)) && ::ChartSaveTemplate((ulong)Chart_ID, PATH + FILENAME) && (::FileLoad(FILENAME, Data) > 0)) ?
           ::ShortArrayToString(Data) : NULL);
  }

Para obter substrings entre as tags especificadas, primeiro determinamos as posições do início e do fim da substring.

  static string StringBetween( string &Str, const string StrBegin, const string StrEnd = NULL )
  {
    int PosBegin = ::StringFind(Str, StrBegin);
    PosBegin = (PosBegin >= 0) ? PosBegin + ::StringLen(StrBegin) : 0;

    const int PosEnd = ::StringFind(Str, StrEnd, PosBegin);

Em seguida, recortamos a substring para retorno e reduzimos a string original para o restante não processado.

    const string Res = ::StringSubstr(Str, PosBegin, (PosEnd >= 0) ? PosEnd - PosBegin : -1);
    Str = (PosEnd >= 0) ? ::StringSubstr(Str, PosEnd + ::StringLen(StrEnd)) : NULL;

    if (Str == "")
      Str = NULL;

    return(Res);
  }

O código completo de todas as funções e classes pode ser encontrado no anexo.

4.3. Função para mudar o status do EA

A alteração do status dos EAs é realizada na função ChangeExpertsMode. Em seus parâmetros, a função especificada recebe a estrutura da operação de negociação e da solicitação de negociação, bem como um novo status para a instalação do EA.

void ChangeExpertsMode(const MqlTradeTransaction &trans,
                       const MqlTradeRequest &request,
                       bool  ExpertMode)
  {
   string comment=request.comment;
   if(StringLen(comment)<=0 && OrderSelect(trans.order))
      comment=OrderGetString(ORDER_COMMENT);      
   string exp_name=(StringLen(comment)>2 ? StringSubstr(comment,2) : NULL);

Em seguida, é realizado um ciclo para iterar todos os gráficos com carregamento de modelos, conforme descrito acima.

   long i_chart=ChartFirst();
   while(i_chart>=0 && !IsStopped())
     {
      if(i_chart==0 || i_chart==chart || (StringLen(exp_name)>0 && ChartSymbol()!=trans.symbol))
        {
         i_chart=ChartNext(i_chart);
         continue;
        }
      string temp=EXPERT::TemplateToString(i_chart,true);
      if(temp==NULL)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

Na próxima etapa, se necessário, verificamos se o EA necessário está no gráfico. Se o EA executado no gráfico não corresponder à solicitação, vamos para o próximo gráfico. Também verificamos o status atual do EA, caso corresponda ao especificado para instalação, avançamos para o próximo gráfico.

      string NewTemplate   =  NULL;
      if(exp_name!=NULL)
        {
         NewTemplate=EXPERT::StringBetween2(temp,NULL,EXPERT_NAME);
         string name=EXPERT::StringBetween(temp,NULL,STRING_END);
         if(name!=exp_name)
           {
            i_chart=ChartNext(i_chart);
            continue;
           }
         NewTemplate+=name+STRING_END;
        }
//---
      NewTemplate+=EXPERT::StringBetween2(temp,NULL,EXPERT_EXPERTMODE);
      bool state  =  (bool)StringToInteger(EXPERT::StringBetween(temp,NULL,STRING_END));
      if(state==ExpertMode)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

Após passar todas as verificações necessárias, criamos um novo modelo com o status especificado de EA. Removemos objetos ocultos do modelo e aplicamos o novo modelo ao gráfico. Após realizar todas as operações, avançamos para o seguinte gráfico.

      NewTemplate+=IntegerToString(ExpertMode)+STRING_END+temp;
      NewTemplate=DeleteHiddenObjects(NewTemplate);
      EXPERT::TemplateApply(i_chart,NewTemplate,true);
//---
      i_chart=ChartNext(i_chart);
     }

Concluído o ciclo de iteração de gráficos, iniciamos a função para coletar informações sobre os EAs em execução, a fim de mostrar ao usuário o novo status dos EA.

   GetExpertsInfo();
  }

O código completo de todas as funções pode ser encontrado no anexo.

4.4. Funções para excluir ordens e fechamentos de posições

As funções para excluir ordens e fechar posições abertas são baseadas no mesmo algoritmo e diferem apenas nos objetos de influência. É por essa razão que, no artigo, proponho considerar apenas um deles. Quando a função DeleteOrders é chamada nos parâmetros, é transferida para ela a estrutura da operação de negociação, a partir da qual o volume da ordem é decodificado na direção das ordens a serem fechadas (de acordo com a tabela de comando da seção 3).

void DeleteOrders(const MqlTradeTransaction &trans)
  {
   int direct=(int)NormalizeDouble(trans.volume/SymbolInfoDouble(trans.symbol,SYMBOL_VOLUME_MIN),0);

Em seguida, é organizado o ciclo de enumeração de todas as ordens colocadas na conta. Nesse ciclo, é verificado se o tipo de ordem está em conformidade com o comando de entrada. Em caso de conformidade, é enviada uma solicitação para excluir a ordem.

   for(int i=total-1;i>=0;i--)
     {
      ulong ticket=OrderGetTicket((uint)i);
      if(ticket<=0)
         continue;
//---
      switch((ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE))
        {
         case ORDER_TYPE_BUY_LIMIT:
         case ORDER_TYPE_BUY_STOP:
         case ORDER_TYPE_BUY_STOP_LIMIT:
           if(direct==2)
              continue;
           Trade.OrderDelete(ticket);
           break;
         case ORDER_TYPE_SELL_LIMIT:
         case ORDER_TYPE_SELL_STOP:
         case ORDER_TYPE_SELL_STOP_LIMIT:
           if(direct==1)
              continue;
           Trade.OrderDelete(ticket);
           break;
        }
     }
  }

O código completo de todas as funções pode ser encontrado no anexo.


Fim do artigo

Esse artigo oferece uma ferramenta para controlar o trabalho de EAs no terminal MetaTrader 5 remotamente. Essa solução permite aumentar a mobilidade dos traders que usam robôs de negociação. A abordagem não padrão aplicada ao uso de funções padrão permite resolver problemas mais amplos sem ter que usar dll.

Links

Expert - biblioteca para o MetaTrader 5

Programas utilizados no artigo:

#
Nome
Tipo
Descrição
1 Expert.mqh Biblioteca de classe Expert - biblioteca para o MetaTrader 5
2 Master.mq5 Expert Advisor EA mestre para gerenciar outros EA em execução no terminal
3 Master.mqproj Arquivo de projeto Projeto EA mestre