Discussão do artigo "Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XXV): processamento de erros retornados pelo servidor de negociação"

 

Novo artigo Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XXV): processamento de erros retornados pelo servidor de negociação foi publicado:

Depois de enviarmos uma ordem de negociação para o servidor, não devemos assumir que o trabalho está concluído, uma vez que é necessário verificar quer os códigos de erro quer a ausência de erros. No artigo, veremos o processamento de erros retornados pelo servidor de negociação e prepararemos a base para a criação de ordens de negociação pendentes.

Nas versões mais recentes do MetaTrader 5, começando com a compilação 2201, o testador pode definir os parâmetros do símbolo no qual está sendo executado o teste. Assim, podemos configurar restrições de negociação para certo símbolo e testar o comportamento da biblioteca quando detectar as restrições definidas para um símbolo.

Para abrir a janela de configurações do símbolo, devemos clicar no botão à direita do item para selecionar o timeframe de teste:

Configuramos o símbolo para negociar apenas usando posições longas, e definimos o limite de volume para posições abertas simultaneamente e ordens pendentes posicionadas numa direção e iguais a 0,5.

Assim, podemos negociar apenas posições longas e ter no mercado um volume total máximo de não mais que 0,5 lotes de posições e ordens de compra. Isto é, ao abrir posições com o lote 0.1, podemos abrir apenas cinco posições ou definir uma ordem pendente de compra e abrir quatro posições:


Aqui, para manter o experimento nítido, certamente, era necessário desativar o fechamento automático de posições quando era excedido o valor de lucro especificado nas configurações. Mas, em princípio, já é evidente que não fomos capazes de abrir posições curtas desde o início, uma vez que recebemos um aviso de que para o símbolo apenas eram permitidas compras e, ao tentar abrir o número de posições, cujo volume total excedia 0,5 lotes, recebíamos uma mensagem indicando que não era possível abrir a posição, uma vez que era excedido o volume total máximo de posições e ordens numa direção.

Autor: Artyom Trishkin

 
Olá, baixei a versão mais recente da biblioteca e do Expert Part_23, coloquei o modo visual no testador e as ordens de mercado são abertas, mas todas as ordens pendentes não são.
As ordens são abertas, mas todas as ordens pendentes não são, no diário escreve "2019.10.27 10:13:32.157 2019.09.23 10:00:02 failed sell stop limit 2.00 RTS-12.19 at 135750 (135800) sl: 135900 tp: 135600 [Invalid expiration].

Símbolos da bolsa, corretora Otkritie, versão 5.00 build 2190.

Desculpe, houve uma continuação da discussão na parte 23, mas eu me atrasei e a perdi. De qualquer forma, baixei a versão 25 e o problema persiste. Na discussão da 23ª parte, você escreveu que é necessário inserir linhas adicionais no onInit'e após determinadas linhas, mas essas linhas destacadas por você em verde não estão no onInit'e nem na 23ª versão nem na 25ª versão:

//--- Configuração das variáveis globais do EA

prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";

for(int i=0;i<TOTAL_BUTT;i++)

{

butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);

butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);

}

lot=NormaliseLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));

magic_number=InpMagic;

stoploss=InpStopLoss;

takeprofit=InpTakeProfit;

distance_pending=InpDistance;

distance_stoplimit=InpDistanceSL;

slippage=InpSlippage;

trailing_stop=InpTrailingStop*Point();

trailing_step=InpTrailingStep*Point();

trailing_start=InpTrailingStart;

stoploss_to_modify=InpStopLossModify;

takeprofit_to_modify=InpTakeProfitModify;


//--- Inicialização da biblioteca DoEasy

OnInitDoEasy();

//--- Verificação e exclusão de objetos gráficos não liberados do Expert Advisor

Se(IsPresentObects(prefix))

ObjectsDeleteAll(0,prefix);


//--- Criar a barra de botões

if(!CreateButtons(InpButtShiftX,InpButtShiftY))

return INIT_FAILED;

//--- Defina o estado do botão de ativação à direita

ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);

//tr.SetCorrectTypeExpiration();

motor.TradingSetCorrectTypeExpiration();

engine.TradingSetCorrectTypeFilling();

//--- Verificação da reprodução do som padrão por substituição de macro e do som personalizado por descrição

motor.PlaySoundByDescription(SND_OK);

Sleep(600);

engine.PlaySoundByDescription(TextByLanguage("The sound of a falling coin 2", "O som de uma moeda caindo 2"));


//---

return(INIT_SUCCEEDED);

 
Alexander:
Olá! Fiz o download da versão mais recente da biblioteca e do Expert Part_23, coloquei o modo visual e o modo de mercado no testador
as ordens são abertas, mas todas as ordens pendentes não são, no diário está escrito "2019.10.27 10:13:32.157 2019.09.23 10:00:02 failed sell stop limit 2.00 RTS-12.19 at 135750 (135800) sl: 135900 tp: 135600 [Invalid expiration].

Símbolos da bolsa, corretora Otkritie, versão 5.00 build 2190.

Desculpe, houve uma continuação da discussão na parte 23, mas eu me atrasei e a perdi. De qualquer forma, baixei a versão 25 e o problema persiste. Na discussão da 23ª parte, você escreveu que é necessário inserir linhas adicionais no onInit'e após determinadas linhas, mas essas linhas destacadas por você em verde não estão no onInit'e nem na versão 23 nem na 25ª, aqui está todo o onInit:

Você está usando o EA de teste do artigo 25 - "TestDoEasyPart25.mq5"?

 
Agora inseri o onInit da versão 23 com as funções adicionais que inseri, mas usei a versão 25, que também não tem as linhas que você destacou em verde na versão 23, e a versão 25 também não funciona como a 23.
 
Alexander:
Agora inseri o onInit da versão 23 com as funções adicionais que inseri, mas usei a versão 25, que também não tem as linhas que você destacou em verde na versão 23, e a versão 25 não funciona tão bem quanto a 23.

Não entendo de quais linhas destacadas em verde você está falando.

E, por favor, não use um EA de teste de outro artigo - cada artigo vem com seu próprio EA para teste - você deve usá-lo e relatar quaisquer erros que encontrar em relação ao EA de teste correto, não de outro artigo.

 

Ok, desculpe, em relação ao último artigo:

Olá! Fiz o download da versão mais recente da biblioteca e do Expert Part_25, coloquei o modo visual no testador e as ordens de mercado foram abertas, mas todas as ordens pendentes não.

as ordens de mercado estão abertas, mas todas as ordens pendentes não estão, o registro diz 2019.11.21 12:59:20.689 2019.11.18 10:00:02 failed sell stop limit 2.00 Si-12.19 at 63972 (64022) sl: 64072 tp: 63972 [Invalid expiration] e 2019.11.21 12:59:20.689 2019.11.18 10:00:02 Tentativa de negociação nº 3. Erro: data de expiração da ordem inválida na solicitação

Símbolos da bolsa, corretora Otkritie, versão 5.00 build 2190.
 
Alexander:

Ok, desculpe, em relação ao último artigo:

Olá! Fiz o download da versão mais recente da biblioteca e do Expert Part_25, coloquei o modo visual e o modo de mercado no testador

as ordens são abertas, mas todas as ordens pendentes não são, o registro diz 2019.11.21 12:59:20.689 2019.11.18 10:00:02 failed sell stop limit 2.00 Si-12.19 at 63972 (64022) sl: 64072 tp: 63972 [Invalid expiration] e 2019.11.21 12:59:20.689 2019.11.18 10:00:02 Tentativa de negociação nº 3. Erro: data de vencimento da ordem inválida na solicitação

Símbolos da bolsa, corretora Otkritie, versão 5.00 build 2190.

Ok, obrigado, vou dar uma olhada nisso.

Você pode anexar aqui uma captura de tela com a especificação do símbolo em que ocorre o erro?

 
Uma captura de tela da especificação Si-12.19 está no arquivo.
Arquivos anexados:
 
Alexander:
Tela da especificação Si-12.19 no arquivo.

Nesta versão da biblioteca, assim como na próxima, não verifiquei os tipos de preenchimento de ordens e os tipos de expiração. As correções serão feitas no próximo artigo (27).

Por enquanto, posso sugerir que, ao enviar uma solicitação de negociação, você deve especificar explicitamente o tipo de expiração. Por exemplo, para colocar uma ordem pendente Sell Limit, você precisa adicioná-la no Expert Advisor de teste:

//--- Se o botão BUTT_SELL_LIMIT for pressionado: Definir SellLimit
else if(button==EnumToString(BUTT_SELL_LIMIT))
  {
   //--- Definir ordem SellLimit
   engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Pending SellLimit" (Limite de venda pendente),"Pending order SellLimit"),0,ORDER_TIME_DAY);
  }

E fazer o mesmo em todas as linhas que contêm a chamada de métodos para definir ordens pendentes.

 

Artem, obrigado pelo trabalho que você fez, muito atencioso.

Para ser sincero, não entendi muito bem o objetivo dos números mágicos compostos....

Suponha que eu vá dividir as negociações em um EA em vários grupos com números mágicos diferentes.

Na abordagem clássica, posteriormente, analiso as ordens e comparo os números mágicos, distribuindo o resultado entre os grupos.

Na biblioteca, entendo que não há muita mudança nessa situação - apenas a alocação desses números pode ser sistematizada de alguma forma? Ou há algo mais que eles afetarão?


E há uma camada específica de funcionalidade que me falta categoricamente em sua biblioteca: a agregação.

Muitas vezes precisamos responder à pergunta: qual é o lucro agregado em um conjunto de parâmetros (magik, símbolo, tipo de ordem). E o lote agregado, por exemplo. E o nível de equilíbrio. E o número de negociações.

Esses valores agregados são necessários em todos os lugares e repetidamente; calculá-los por amostragem em cada novo método é um exagero óbvio.

Seria ótimo ter um conjunto de agregações, configurando cada uma com um conjunto de critérios de amostragem (símbolo, tipo, magik, número do grupo) e fornecer acesso a elas no CEngine - e atualizá-las uma vez no Refresh.

Se você não tivesse planos para esse tipo de refinamento, eu poderia finalizar e compartilhar..... mas como temos um estilo de código significativamente diferente, essa parte pareceria estranha.

 
rigal:

Artem, obrigado pelo trabalho que você fez, muito atencioso.

Não entendi muito bem o objetivo das magias compostas, para ser honesto....

Suponha que eu vá dividir as negociações em um Expert Advisor em vários grupos com números mágicos diferentes.

Na abordagem clássica, posteriormente, analiso as ordens e comparo os números mágicos, distribuindo o resultado entre os grupos.

Na biblioteca, entendo que não há muita mudança nessa situação - apenas a alocação desses números pode ser sistematizada de alguma forma? Ou há algo mais que eles afetarão?


E há também uma certa camada de funcionalidade que me falta categoricamente em sua biblioteca: agregação.

Muitas vezes, precisamos responder à pergunta: qual é o lucro agregado em um conjunto de parâmetros (magik, símbolo, tipo de ordem). E o lote agregado, por exemplo. E o nível de equilíbrio. E o número de negociações.

Esses valores agregados são necessários em todos os lugares e repetidamente; calculá-los por amostragem em cada novo método é um exagero óbvio.

Seria ótimo ter um conjunto de agregações, configurando cada uma com um conjunto de critérios de amostragem (símbolo, tipo, magik, número do grupo) e fornecer acesso a elas no CEngine - e atualizá-las uma vez no Refresh.

Se você não tivesse planos para esse tipo de refinamento, eu poderia finalizar e compartilhar..... mas como temos um estilo de código significativamente diferente, essa parte pareceria estranha.

Sobre as informações registradas no valor do Magic Number:

você pode usar uma mágica diferente para cada grupo para criar grupos diferentes. Por exemplo, se o magik do consultor for 123, o magik do primeiro grupo será 124, o magik do segundo grupo será 125, o terceiro grupo será 126 e assim por diante.
A biblioteca oferece uma maneira diferente de criar grupos diferentes - o número de cada subgrupo é armazenado diretamente no valor do número mágico. Em seguida, o número mágico do EA também é um identificador de grupo, mas é colocado em um grupo independente chamado MagicID - o identificador do número mágico do EA. E há mais dois grupos. Cada um deles tem 15 subgrupos. E cada um dos subgrupos pode ter seu próprio identificador.

Isso proporcionarámais flexibilidade ao trabalhar com grupos.

Por exemplo: queremos mover uma grade de ordens pendentes para trás do preço - nós as adicionamos ao grupo 1 no subgrupo 1. O grupo 1 se move atrás do preço. O subgrupo 1 se move ao longo da MA. Agora, queremos mover algumas dessas ordens que se movem atrás do preço (grupo 1) pelo Parabolic SAR. Damos a elas o subgrupo 2. Então, o grupo 1 se move após o preço, mas o subgrupo 1 se move pela MA e o subgrupo 2 se move pelo SAR Parabólico.

As ordens são acionadas, transformando-se em posições - você pode definir seus próprios grupos para modificação de stoplosses e seus próprios subgrupos nesse grupo para modificação por valores diferentes. Os algoritmos de modificação são escritos em subgrupos.

Em geral, um voo de fantasia. Você também pode usar uma mágica simples, mas terá que pensar na lógica de rastreamento de diferentes grupos por conta própria.

Sobre a segunda pergunta:

Existe uma classe CSelect. Ela está disponível no programa e fornece métodos de seleção e pesquisa, sobre os quais você escreve, de todas as coleções existentes: Account, Event, Order, Symbol.

Você pode selecionar os objetos de cada coleção em uma lista com base em todos os critérios. Na lista criada, você pode selecionar novamente, refinando os critérios, e pode encontrar os valores máximo e mínimo para um critério de seleção.

No entanto, haverá outras funções de usuário (muito mais tarde) para acesso rápido e conveniente a todas as propriedades de todas as coleções e pesquisa nelas.

Mas, por enquanto, somente por meio do CSelect, e quando você precisar. A classe é estática, portanto, o acesso a seus métodos é feito por meio de ":::" Por exemplo, CSelect::ByOrderProperty().

Sim, a propósito, há um exemplo de uso no programa, bem no EA de teste - por exemplo, em suas funções finais:

//+------------------------------------------------------------------+
//| Procurar os pedidos pendentes mais distantes
//+------------------------------------------------------------------+
void TrailingOrders(void)
  {
   MqlTick tick;
   if(!SymbolInfoTick(Symbol(),tick))
      return;
   double stop_level=StopLevel(Symbol(),2)*Point();
//--- Obter a lista de todas as ordens definidas
   CArrayObj* list=engine.GetListMarketPendings();
   //--- Selecione apenas ordens pelo símbolo atual na lista
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
//--- Selecione apenas ordens na direção de compra da lista
   CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_BUY,EQUAL);
   //--- Ordenar a lista por distância do preço em pips (por lucro em pips)
   list_buy.Sort(SORT_BY_ORDER_PROFIT_PT);
   //--- Obtenha o índice da ordem na direção Buy com a maior distância
   int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_PT);
   if(index_buy>WRONG_VALUE)
     {
      COrder* buy=list_buy.At(index_buy);
      if(buy!=NULL)
        {
         //--- Se a ordem estiver definida abaixo do preço (BuyLimit) e precisar ser "levantada" para trás do preço
         if(buy.TypeOrder()==ORDER_TYPE_BUY_LIMIT)
           {
            //--- Calcular o novo preço de configuração da ordem e os níveis de parada a partir do novo preço
            double price=NormalizeDouble(tick.ask-trailing_stop,Digits());
            double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0);
            double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0);
            //--- Se o preço calculado for menor do que a distância StopLevel, adiado a partir do preço de configuração da ordem Ask (a distância StopLevel é observada)
            if(price<tick.ask-stop_level) 
              {
               //--- Se o preço calculado for maior do que a etapa de arrasto adiada do preço de configuração do pedido - modifique o preço de configuração do pedido
               if(price>buy.PriceOpen()+trailing_step)
                 {
                  engine.ModifyOrder((ulong)buy.Ticket(),price,sl,tp,-1);
                 }
              }
           }
         //--- Se a ordem estiver definida acima do preço (BuyStop e BuyStopLimit) e precisar ser "abaixada" atrás do preço
         else
           {
            //--- Calcular o novo preço de configuração da ordem e os níveis de parada a partir do novo preço
            double price=NormalizeDouble(tick.ask+trailing_stop,Digits());
            double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0);
            double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0);
            //--- Se o preço calculado for maior do que a distância do StopLevel, adiado a partir do preço de configuração da ordem Ask (a distância do StopLevel é observada)
            if(price>tick.ask+stop_level) 
              {
               //--- Se o preço calculado for menor do que a etapa de arrasto adiada do preço de configuração do pedido - modifique o preço de configuração do pedido
               if(price<buy.PriceOpen()-trailing_step)
                 {
                  engine.ModifyOrder((ulong)buy.Ticket(),price,sl,tp,-1);
                 }
              }
           }
        }
     }
//--- Selecione apenas ordens na direção Sell da lista
   CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_SELL,EQUAL);
   //--- Ordenar a lista por distância do preço em pips (por lucro em pips)
   list_sell.Sort(SORT_BY_ORDER_PROFIT_PT);
   //--- Obtenha o índice da ordem na direção de Sell com a maior distância
   int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_PT);
   if(index_sell>WRONG_VALUE)
     {
      COrder* sell=list_sell.At(index_sell);
      if(sell!=NULL)
        {
         //--- Se a ordem estiver definida acima do preço (SellLimit), e ela deve ser "abaixada" atrás do preço
         if(sell.TypeOrder()==ORDER_TYPE_SELL_LIMIT)
           {
            //--- Calcular o novo preço de configuração da ordem e os níveis de parada a partir do novo preço
            double price=NormalizeDouble(tick.bid+trailing_stop,Digits());
            double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0);
            double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0);
            //--- Se o preço calculado for maior do que a distância StopLevel, adiado a partir do preço de configuração da ordem Bid (a distância StopLevel é observada)
            if(price>tick.bid+stop_level) 
              {
               //--- Se o preço calculado for menor do que a etapa de arrasto adiada do preço de configuração do pedido - modifique o preço de configuração do pedido
               if(price<sell.PriceOpen()-trailing_step)
                 {
                  engine.ModifyOrder((ulong)sell.Ticket(),price,sl,tp,-1);
                 }
              }
           }
         //--- Se a ordem estiver definida abaixo do preço (SellStop e SellStopLimit) e ela deve ser "levantada" atrás do preço
         else
           {
            //--- Calcular o novo preço de configuração da ordem e os níveis de parada a partir do novo preço
            double price=NormalizeDouble(tick.bid-trailing_stop,Digits());
            double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0);
            double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0);
            //--- Se o preço calculado for menor do que a distância StopLevel, adiado a partir do preço de configuração da ordem Bid (a distância StopLevel é observada)
            if(price<tick.bid-stop_level) 
              {
               //--- Se o preço calculado for maior do que a etapa de arrasto adiada do preço de configuração do pedido - modifique o preço de configuração do pedido
               if(price>sell.PriceOpen()+trailing_step)
                 {
                  engine.ModifyOrder((ulong)sell.Ticket(),price,sl,tp,-1);
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+