Problema com fechamento de posição em conta Hedge

 

Olá pessoal, tudo bem?

Estou enfrentando um problema com uma função que desenvolvi para fechar todas as posições abertas e ordens pendentes quando chamada. Em simulações e backtests, ela funciona perfeitamente. No entanto, em contas reais, a função não consegue fechar a posição existente, o que gera um problema sério: múltiplas posições indevidas são abertas.

O que acontece é o seguinte:

  1. A função tenta abrir uma posição para fechar a posição atual, mas não consegue finalizar a primeira.
  2. Em seguida, tenta encerrar novamente a posição original e a oposta, resultando em várias posições em cascata.

Observações:

  1. A função é chamada às 17:55hr.
  2. O EA opera no WDO.

Alguém já passou por algo semelhante ou sabe qual pode ser a causa desse comportamento? Agradeço antecipadamente pela ajuda!

void CancelAllPendingOrdersAndClosePositions()
  {
// Cancela todas as ordens pendentes
   long totalOrders = OrdersTotal();
   for(int i = (int)totalOrders - 1; i >= 0; i--)
     {
      ulong ticket = OrderGetTicket(i);
      if(ticket == 0)
         continue;

      if(!OrderSelect(ticket))
         continue;

      // Verifica se a ordem pertence ao EA
      if(OrderGetInteger(ORDER_MAGIC) == MagicNumber && OrderGetString(ORDER_SYMBOL) == _Symbol)
        {
         ENUM_ORDER_TYPE orderType = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);

         // Cancela ordens pendentes (Buy Limit ou Sell Limit)
         if(orderType == ORDER_TYPE_BUY_LIMIT || orderType == ORDER_TYPE_SELL_LIMIT)
           {
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);

            request.action = TRADE_ACTION_REMOVE;
            request.order  = ticket;

            if(!OrderSend(request, result))
               Print("Falha ao cancelar ordem pendente: ", ticket, " Erro: ", result.comment);
            else
               Print("Ordem pendente cancelada: ", ticket);
           }
        }
     }

// Fecha todas as posições abertas
   long totalPositions = PositionsTotal();
   for(int i = (int)totalPositions - 1; i >= 0; i--)
     {
      ulong positionTicket = PositionGetTicket(i);
      if(PositionSelectByTicket(positionTicket))
        {
         // Verifica se a posição pertence ao EA
         if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == _Symbol)
           {
            ulong positionTicket = PositionGetTicket(i);
            ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
            double volume = PositionGetDouble(POSITION_VOLUME);

            // Determina o tipo de ordem oposta
            ENUM_ORDER_TYPE oppositeType;
            double price;
            if(positionType == POSITION_TYPE_BUY)
              {
               oppositeType = ORDER_TYPE_SELL;
               price = SymbolInfoDouble(_Symbol, SYMBOL_BID); // Preço para venda
              }
            else
               if(positionType == POSITION_TYPE_SELL)
                 {
                  oppositeType = ORDER_TYPE_BUY;
                  price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Preço para compra
                 }
               else
                 {
                  PrintFormat("Tipo de posição desconhecido: ", positionType);
                  continue;
                 }

            // Prepara a solicitação para abrir a ordem oposta a mercado
            MqlTradeRequest openRequest;
            MqlTradeResult openResult;
            ZeroMemory(openRequest);
            ZeroMemory(openResult);

            openRequest.action = TRADE_ACTION_DEAL;
            openRequest.symbol = _Symbol;
            openRequest.volume = volume;
            openRequest.type = oppositeType;
            openRequest.price = price;
            openRequest.deviation = 10; // Tolerância em pontos para preenchimento imediato
            openRequest.magic = MagicNumber;
            openRequest.comment = "Estratégia - Ordem oposta para fechar posição";
            openRequest.type_filling = ORDER_FILLING_FOK;
            openRequest.type_time = ORDER_TIME_DAY;

            // Envia a ordem oposta
            if(!OrderSend(openRequest, openResult))
              {
               PrintFormat("Falha ao abrir ordem oposta para fechar posição.", openResult.retcode, openResult.comment);
               continue;
              }
            else
               PrintFormat("Ordem oposta aberta para fechar posição. Novo ticket", openResult.order);

            // Prepara a solicitação para fechar a posição original com a ordem oposta
            MqlTradeRequest closeRequest;
            MqlTradeResult closeResult;
            ZeroMemory(closeRequest);
            ZeroMemory(closeResult);

            closeRequest.action = TRADE_ACTION_CLOSE_BY;
            closeRequest.position = positionTicket;
            closeRequest.position_by = openResult.order;
            closeRequest.symbol = _Symbol;
            closeRequest.magic = MagicNumber;

            // Envia a solicitação para fechar as posições
            if(!OrderSend(closeRequest, closeResult))
               PrintFormat("Falha ao fechar posição com a ordem oposta.", positionTicket, openResult.order, closeResult.retcode, closeResult.comment);
            else
              {
               PrintFormat("Posição fechada com a ordem oposta.", positionTicket, openResult.order);
               isPositioned = false;
              }
           }
        }
     }
  }


Abaixo está prints do erro que acontece:


 
Jvmelo:
closeRequest.position_by = openResult.order;
troca o final "order" por "deal"
 
Ricardo Rodrigues Lucca #:
troca o final "order" por "deal"

Olá Ricardo, agradeço a resposta.

Não entendi direito, em que ponto trocar a order por deal?

Seria nessa linha aqui?

 closeRequest.position_by = openResult.order;
Se for, poderia explicar o motivo?
 
Porque você não preenche o openRequest.position com o id da posição diretamente? Assim esta primeira ordem já fecharia a posição diretamente e evitaria ter que fazer a segunda ordem close by.
 
JULIANO MARANGONI #:
Porque você não preenche o openRequest.position com o id da posição diretamente? Assim esta primeira ordem já fecharia a posição diretamente e evitaria ter que fazer a segunda ordem close by.

Olá Juliano, funcionou perfeitamente em simulação e backtests, igual a abordagem anterior. Irei aguardar uma situação em mundo real para ver se irá funcionar.

Obrigado pela ajuda.

 
Jvmelo #:

Olá Ricardo, agradeço a resposta.

Não entendi direito, em que ponto trocar a order por deal?

Seria nessa linha aqui?

Se for, poderia explicar o motivo?

Isso pra ficar assim

closeRequest.position_by = openResult.deal;
Voce esta pedindo pra fechar a posicao com o id de uma ordem, o que me parece ilegal já que a ordem não necessariamente foi executada. O deal parece fazer mais sentido. Também falar que o type filling sendo FOK deveria ter um teste para ver que o negocio foi fechado, já que é mais alta a possibilidade de não ter sido por conta do FOK. Se ele não foi, não vai existir ordem e nem negocio.