Gerenciamento de pedidos - É simples

Andrey Khatimlianskii | 18 fevereiro, 2016


1. Introdução


Todo expert de transações possui um bloco de controle de posições abertas. Ele é a busca entre todos os pedidos no interior de um ciclo, escolhendo a sua "própria" posição através do símbolo e do valor do MagicNumber, e então modificando ou fechando a mesma. Esses blocos possuem aparência bastante semelhante, e frequentemente cumprem as mesmas funções. É por este motivo que esta parte repetida do código pode ser transferida do expert para a função. Isso irá simplificar significativamente a escrita de experts e tornar os códigos dos experts muito mais enxutos.

Primeiramente vamos dividir a tarefa em três estágios, que são diferentes em cumplicidade e funções. Esses três estágios irão se correlacionar com três tipos de experts:

  • Experts que podem abrir apenas uma posição por vez
  • Experts que podem abrir uma posição de cada tipo por vez (por exemplo, uma posição longa e uma curta)
  • Experts que podem abrir qualquer quantidade de posições simultaneamente

2. Uma posição


Há muitas estratégias que utilizam apenas uma posição aberta. O seus blocos de controle são bastante simples, mas ainda assim a sua escrita exige tempo e atenção.

Vamos examinar um expert simples, cujo sinal de abertura é a intersecção das linhas MACD (linha de sinal e linha básica), e simplificar o seu bloco de controle. Essa é a sua aparência anterior:

extern int  _MagicNumber = 1122;
 
int start()
{
    //---- Memorize indicator values for further analysis
    double MACD_1 = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE,
                          MODE_MAIN, 1 );
    double MACD_2 = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE,
                          MODE_MAIN, 2 );
 
    int _GetLastError = 0, _OrdersTotal = OrdersTotal();
    //---- search among all open positions
    for ( int z = _OrdersTotal - 1; z >= 0; z -- )
    {
// if an error occurs at finding the position, go to the next one
        if ( !OrderSelect( z, SELECT_BY_POS ) )
        {
          _GetLastError = GetLastError();
          Print("OrderSelect( ", z, ", SELECT_BY_POS ) - Error #",
                _GetLastError );
            continue;
        }
 
// if the position was opened not for the current symbol, 
// skip it
        if ( OrderSymbol() != Symbol() ) continue;
 
// if MagicNumber does not equal to _MagicNumber, skip 
// this position
        if ( OrderMagicNumber() != _MagicNumber ) continue;
 
        //---- if BUY position is opened,
        if ( OrderType() == OP_BUY )
        {
            //---- if MACD meets zero line top-down,
            if(NormalizeDouble(MACD_1, Digits + 1) <  0.0 && 
               NormalizeDouble( MACD_2, Digits + 1) >= 0.0)
            {
                //---- close the position
                if(!OrderClose( OrderTicket(), OrderLots(), 
                   Bid, 5, Green))
                {
                   _GetLastError = GetLastError();
                   Alert("Error OrderClose № ", _GetLastError);
                   return(-1);
                }
            }
// if signal has not changed, exit: it's too early for 
// opening a new position
            else
            { return(0); }
        }
        //---- if SELL position is opened,
        if ( OrderType() == OP_SELL )
        {
            //---- if MACD meets zero line bottom-up,
            if(NormalizeDouble(MACD_1, Digits + 1) >  0.0 && 
               NormalizeDouble(MACD_2, Digits + 1 ) <= 0.0)
            {
              //---- close the position
              if(!OrderClose( OrderTicket(), OrderLots(), 
                 Ask, 5, Red))
              {
                _GetLastError = GetLastError();
                Alert( "Error OrderClose № ", _GetLastError );
                return(-1);
              }
            }
// if signal has not changed, exit: it's too early to open 
// a new position
            else return(0);
        }
    }
 
//+------------------------------------------------------------------+
//| if execution has reached this point, this means no open position |
//| check whether it is possible to open a position                  |
//+------------------------------------------------------------------+
 
    //---- if MACD meets zero line bottom-up,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
    {
        //---- open a BUY position
        if(OrderSend( Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, 
           "MACD_test", _MagicNumber, 0, Green ) < 0)
        {
            _GetLastError = GetLastError();
            Alert( "Error OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }
    //---- if MACD meets zero line top-down,
    if(NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0)
    {
        //---- open a SELL position
        if(OrderSend( Symbol(), OP_SELL, 0.1, Bid, 5, 0.0, 0.0,
           "MACD_test", 
              _MagicNumber, 0, Red ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Error OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }
 
    return(0);
}

Agora nós temos que escrever uma função capaz de substituir o bloco de controle de posições. A função deve realizar buscas entre todos os pedidos, encontrar o pedido necessário e memorizar todas as suas características em variáveis globais. Ele ficará parecido com isto:

int _Ticket = 0, _Type = 0; double _Lots = 0.0, 
_OpenPrice = 0.0, _StopLoss = 0.0;
double _TakeProfit = 0.0; datetime _OpenTime = -1; 
double _Profit = 0.0, _Swap = 0.0;
double _Commission = 0.0; string _Comment = ""; 
datetime _Expiration = -1;
 
void OneOrderInit( int magic )
{
    int _GetLastError, _OrdersTotal = OrdersTotal();
 
    _Ticket = 0; _Type = 0; _Lots = 0.0; _OpenPrice = 0.0; 
_StopLoss = 0.0;
    _TakeProfit = 0.0; _OpenTime = -1; _Profit = 0.0; 
_Swap = 0.0;
    _Commission = 0.0; _Comment = ""; _Expiration = -1;
 
    for ( int z = _OrdersTotal - 1; z >= 0; z -- )
    {
        if ( !OrderSelect( z, SELECT_BY_POS ) )
        {
          _GetLastError = GetLastError();
          Print("OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", 
                _GetLastError );
          continue;
        }
        if(OrderMagicNumber() == magic && OrderSymbol() == 
           Symbol())
        {
          _Ticket    = OrderTicket();
          _Type      = OrderType();
          _Lots      = NormalizeDouble( OrderLots(), 1 );
          _OpenPrice = NormalizeDouble( OrderOpenPrice(), Digits);
          _StopLoss = NormalizeDouble( OrderStopLoss(), Digits);
          _TakeProfit = NormalizeDouble( OrderTakeProfit(), Digits);
          _OpenTime   = OrderOpenTime();
          _Profit     = NormalizeDouble( OrderProfit(), 2 );
          _Swap       = NormalizeDouble( OrderSwap(), 2 );
          _Commission = NormalizeDouble( OrderCommission(), 2 );
          _Comment    = OrderComment();
          _Expiration = OrderExpiration();
          return;
        }
    }
}

Como você pode ver, é bastante fácil: há 11 variáveis, e cada uma delas armazena o valor de uma característica de posição (# do bilhete, tipo, tamanho do lote, etc). Essas variáveis são "zeroizadas" no momento da inicialização da função. Isso é necessário porque as variáveis são declaradas a nível global, e não são "zeroizadas" no momento da chamada da função. Mas nós não precisamos de informações relativas ao tick anterior, todos os dados devem ser recentes. Em seguida, é realizada uma busca padrão entre todas as posições abertas e, caso o símbolo e o valor do MagicNumber coincidam com aqueles procurados, as características são memorizadas nas variáveis correspondentes.


Agora vamos anexar essa função ao nosso expert advisor:


extern int  _MagicNumber = 1122;
 
#include <OneOrderControl.mq4>
 
int start()
{
    int _GetLastError = 0;
    
// Memorize parameters of the open position (if available)
    OneOrderInit( _MagicNumber );
 
    //---- Memorize indicator values for further analysis
    double MACD_1 = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE, 
                          MODE_MAIN, 1 );
    double MACD_2 = iMACD(Symbol(), 0, 12, 26, 9, PRICE_CLOSE, 
                          MODE_MAIN, 2 );
 
    // Now, instead of searching in positions, just see whether 
    // there is an open position:
    if ( _Ticket > 0 )
    {
        //---- if a BUY position is opened,
        if ( _Type == OP_BUY )
        {
            //---- if MACD meets zero line top-down,
            if(NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
               NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0)
            {
                //---- close position
                if(!OrderClose( _Ticket, _Lots, Bid, 5, Green))
                {
                  _GetLastError = GetLastError();
                  Alert( "Error OrderClose № ", _GetLastError);
                  return(-1);
                }
            }
            // if signal has not changed, exit: it's too early 
            // to open a new position
            else return(0);
        }
        //---- if a SELL position is opened,
        if ( _Type == OP_SELL )
        {
            //---- if MACD meets zero line bottom-up,
            if(NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
               NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0)
            {
                //---- close the position
                if(!OrderClose( _Ticket, _Lots, Ask, 5, Red))
                {
                    _GetLastError = GetLastError();
                    Alert( "Error OrderClose № ", _GetLastError);
                    return(-1);
                }
            }
            // if signal has not changed, exit: it's too early 
            // to open a new position
            else return(0);
        }
    }
    // if there is no position opened by the expert 
    // ( _Ticket == 0 )
    // if MACD meets zero line bottom-up,
    if(NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
       NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0)
    {
        //---- open a BUY position
        if(OrderSend(Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0,
           "CrossMACD", _MagicNumber, 0, Green ) < 0)
        {
            _GetLastError = GetLastError();
            Alert( "Error OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }
    //---- if MACD meets zero line top-down,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
    {
        //---- open a SELL position
        if(OrderSend(Symbol(), OP_SELL, 0.1, Bid, 5, 0.0, 0.0,
           "CrossMACD", 
              _MagicNumber, 0, Red ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Error OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }
 
    return(0);
}

Como você pode ver, agora o código do expert está muito mais compacto e legível por humanos. Este é o caso mais simples.


Agora vamos cumprir a próxima tarefa.

3. Uma posição de cada tipo


Nós precisamos de um expert mais complexo para implementar a outra função. Ele precisa abrir várias posições de tipos diferentes e trabalhar com elas. Abaixo temos o algoritmo do expert:

  • quando ele é inicializado, o expert deve fazer dois pedidos pendentes: BuyStop no nível de Ask+20 pontos e um SellStop no nível de Bid+20 pontos;
  • caso um dos pedidos for acionado, outro precisa ser deletado;
  • a posição aberta deve ser acompanhada de um limite móvel; e
  • após a posição ter sido fechada por StopLoss ou TakeProfit, voltar ao início, ou seja, fazer dois pedidos pendentes novamente.

O código do expert é fornecido abaixo:


extern int    _MagicNumber = 1123;

extern double Lot          = 0.1;
extern int    StopLoss     = 60;
// distance to StopLoss in points (0 - disable)
extern int    TakeProfit=100;
// distance to TakeProfit in points (0 - disable)
extern int    TrailingStop=50;
// Trailing Stop in points (0 - disable)

extern int    Luft=20;
// distance to the level at which the pending order was placed

int start()
  {
// Variables, in which tickets of orders of each type will be 
// memorized
   int BuyStopOrder=0,SellStopOrder=0,BuyOrder=0,SellOrder=0;
   int _GetLastError=0,_OrdersTotal=OrdersTotal();
// search in all open positions and memorize, positions of which 
// type have already been opened:
   for(int z=_OrdersTotal-1; z>=0; z --)
     {
      // if an error occurs at searching for a position, go 
      // to the next one
      if(!OrderSelect(z,SELECT_BY_POS))
        {
         _GetLastError=GetLastError();
         Print("OrderSelect(",z,", SELECT_BY_POS) - Error #",_GetLastError);
         continue;
        }

      // if the position was opened not for the current symbol, skip it
      if(OrderSymbol()!=Symbol()) continue;

      // if the MagicNumber is not equal to _MagicNumber, skip this 
      // position
      if(OrderMagicNumber()!=_MagicNumber) continue;

      // depending on the position type, change value of the 
      // variable:
      switch(OrderType())
        {
         case OP_BUY:      BuyOrder      = OrderTicket(); break;
         case OP_SELL:     SellOrder     = OrderTicket(); break;
         case OP_BUYSTOP:  BuyStopOrder  = OrderTicket(); break;
         case OP_SELLSTOP: SellStopOrder = OrderTicket(); break;
        }
     }

//---- If we have both pending orders, quit, 
//---- we have to wait until one of them triggers
   if( BuyStopOrder > 0 && SellStopOrder > 0 ) return(0);

// search in all open positions for the second time - now 
// we will work with them:
   _OrdersTotal=OrdersTotal();
   for(z=_OrdersTotal-1; z>=0; z --)
     {
      // if an error occurs in searching a position, go to 
      // the next one
      if(!OrderSelect(z,SELECT_BY_POS))
        {
         _GetLastError=GetLastError();
         Print("OrderSelect(",z,", SELECT_BY_POS) - Error #",_GetLastError);
         continue;
        }

      // if the position was opened not for the current symbol,
      // skip it
      if(OrderSymbol()!=Symbol()) continue;

      // if the MagicNumber does not equal to _MagicNumber, 
      // skip this position
      if(OrderMagicNumber()!=_MagicNumber) continue;

      // depending on the position type, change the variable 
      // value:
      switch(OrderType())
        {
         //---- if there is an open BUY position,
         case OP_BUY:
           {
            // if the SellStop order has not been deleted 
            // yet, delete it:
            if(SellStopOrder>0)
              {
               if(!OrderDelete(SellStopOrder))
                 {
                  Alert("OrderDelete Error #",GetLastError());
                  return(-1);
                 }
              }
            // check whether the StopLoss should be moved:
            // if the size of Trailing Stop is not very small,
            if(TrailingStop>MarketInfo(Symbol(),
               MODE_STOPLEVEL))
              {
               // if the profit exceeds the TrailingStop 
               // points,
               if(NormalizeDouble(Bid-OrderOpenPrice(),Digits)>NormalizeDouble(TrailingStop*Point,Digits))
                 {
                  // if the new StopLoss level exceeds 
                  // the current level of that for the 
                  // position
                  // (or if the position does not have 
                  // a StopLoss),
                  if(NormalizeDouble(Bid-TrailingStop*Point,Digits)>OrderStopLoss() || OrderStopLoss()<=0.0)
                    {
                     //---- modify the order
                     if(!OrderModify(OrderTicket(),OrderOpenPrice(),NormalizeDouble(Bid-TrailingStop*Point,Digits),OrderTakeProfit(),OrderExpiration()))
                       {
                        Alert("OrderModify Error #",GetLastError());
                        return(-1);
                       }
                    }
                 }
              }
            // if there is an open position, quit, there 
            // is nothing to do
            return(0);
           }
         // The next block is absolutely the same as the 
         // block processing a BUY position,
         // this is why we do not comment on it...
         case OP_SELL:
           {
            if(BuyStopOrder>0)
              {
               if(!OrderDelete(BuyStopOrder))
                 {
                  Alert("OrderDelete Error #",GetLastError());
                  return(-1);
                 }
              }
            if(TrailingStop>MarketInfo(Symbol(),
               MODE_STOPLEVEL))
              {
               if(NormalizeDouble(OrderOpenPrice()-Ask,Digits)>NormalizeDouble(TrailingStop*Point,Digits))
                 {
                  if(NormalizeDouble(Ask+TrailingStop*Point,Digits)<OrderStopLoss() || OrderStopLoss()<=0.0)
                    {
                     if(!OrderModify(OrderTicket(),OrderOpenPrice(),NormalizeDouble(Ask+TrailingStop*Point,Digits),OrderTakeProfit(),OrderExpiration()))
                       {
                        Alert("OrderModify Error #",GetLastError());
                        return(-1);
                       }
                    }
                 }
              }
            return(0);
           }
        }
     }

//+------------------------------------------------------------------+
//| If execution has reached this point, it means there are no       |
//| pending orders or open positions                                 |
//+------------------------------------------------------------------+
//---- Place BuyStop and SellStop:
   double _OpenPriceLevel,_StopLossLevel,_TakeProfitLevel;
   _OpenPriceLevel=NormalizeDouble(Ask+Luft*Point,Digits);

   if(StopLoss>0)
     { _StopLossLevel=NormalizeDouble(_OpenPriceLevel-StopLoss*Point,Digits); }
   else
     { _StopLossLevel=0.0; }

   if(TakeProfit>0)
     { _TakeProfitLevel=NormalizeDouble(_OpenPriceLevel+TakeProfit*Point,Digits); }
   else
     { _TakeProfitLevel=0.0; }

   if(OrderSend(Symbol(),OP_BUYSTOP,Lot,_OpenPriceLevel,5,_StopLossLevel,_TakeProfitLevel,"",_MagicNumber)<0)
     {
      Alert("OrderSend Error #",GetLastError());
      return(-1);
     }

   _OpenPriceLevel=NormalizeDouble(Bid-Luft*Point,Digits);

   if(StopLoss>0)
     { _StopLossLevel=NormalizeDouble(_OpenPriceLevel+StopLoss*Point,Digits); }
   else
     { _StopLossLevel=0.0; }

   if(TakeProfit>0)
     { _TakeProfitLevel=NormalizeDouble(_OpenPriceLevel-TakeProfit*Point,Digits); }
   else
     { _TakeProfitLevel=0.0; }

   if(OrderSend(Symbol(),OP_SELLSTOP,Lot,_OpenPriceLevel,
      5,_StopLossLevel,_TakeProfitLevel,"",_MagicNumber)<0)
     {
      Alert("OrderSend Error #",GetLastError());
      return(-1);
     }
   return(0);
  }

Vamos agora escrever a função que simplificaria o bloco de controle de posições abertas. Ela deve encontrar um pedido de cada tipo e armazenar as suas características em variáveis globais. Ela ficará parecido com isto:


 
// global variables in which order characteristics will be stored:
int _BuyTicket = 0, _SellTicket = 0, _BuyStopTicket = 0;
int _SellStopTicket = 0, _BuyLimitTicket = 0, _SellLimitTicket = 0;
 
double _BuyLots = 0.0, _SellLots = 0.0, _BuyStopLots = 0.0; 
double _SellStopLots = 0.0, _BuyLimitLots = 0.0, 
_SellLimitLots = 0.0;
 
double _BuyOpenPrice = 0.0, _SellOpenPrice = 0.0, 
_BuyStopOpenPrice = 0.0;
double _SellStopOpenPrice = 0.0, _BuyLimitOpenPrice = 0.0, 
_SellLimitOpenPrice = 0.0;
 
double _BuyStopLoss = 0.0, _SellStopLoss = 0.0, _BuyStopStopLoss = 0.0;
double _SellStopStopLoss = 0.0, _BuyLimitStopLoss = 0.0, _SellLimitStopLoss = 0.0;
 
double _BuyTakeProfit = 0.0, _SellTakeProfit = 0.0, 
_BuyStopTakeProfit = 0.0;
double _SellStopTakeProfit = 0.0, _BuyLimitTakeProfit = 0.0,
 _SellLimitTakeProfit = 0.0;
 
datetime _BuyOpenTime = -1, _SellOpenTime = -1, 
_BuyStopOpenTime = -1;
datetime _SellStopOpenTime = -1, _BuyLimitOpenTime = -1, 
_SellLimitOpenTime = -1;
 
double _BuyProfit = 0.0, _SellProfit = 0.0, _BuySwap = 0.0, 
_SellSwap = 0.0;
double _BuyCommission = 0.0, _SellCommission = 0.0;
 
string _BuyComment = "", _SellComment = "", _BuyStopComment = ""; 
string _SellStopComment = "", _BuyLimitComment = "", 
_SellLimitComment = "";
 
datetime _BuyStopExpiration = -1, _SellStopExpiration = -1;
datetime _BuyLimitExpiration = -1, _SellLimitExpiration = -1;
 
void OneTypeOrdersInit( int magic )
{
// zeroizing of variables:
    _BuyTicket = 0; _SellTicket = 0; _BuyStopTicket = 0;
    _SellStopTicket = 0; _BuyLimitTicket = 0; _SellLimitTicket = 0;
 
    _BuyLots = 0.0; _SellLots = 0.0; _BuyStopLots = 0.0; 
    _SellStopLots = 0.0; _BuyLimitLots = 0.0; _SellLimitLots = 0.0;
 
    _BuyOpenPrice = 0.0; _SellOpenPrice = 0.0; _BuyStopOpenPrice = 0.0;
    _SellStopOpenPrice = 0.0; _BuyLimitOpenPrice = 0.0; 
_SellLimitOpenPrice = 0.0;
 
    _BuyStopLoss = 0.0; _SellStopLoss = 0.0; _BuyStopStopLoss = 0.0;
    _SellStopStopLoss = 0.0; _BuyLimitStopLoss = 0.0; 
_SellLimitStopLoss = 0.0;
 
    _BuyTakeProfit = 0.0; _SellTakeProfit = 0.0; 
_BuyStopTakeProfit = 0.0;
    _SellStopTakeProfit = 0.0; _BuyLimitTakeProfit = 0.0; 
_SellLimitTakeProfit = 0.0;
 
    _BuyOpenTime = -1; _SellOpenTime = -1; _BuyStopOpenTime = -1;
    _SellStopOpenTime = -1; _BuyLimitOpenTime = -1; 
_SellLimitOpenTime = -1;
 
    _BuyProfit = 0.0; _SellProfit = 0.0; _BuySwap = 0.0; 
_SellSwap = 0.0;
    _BuyCommission = 0.0; _SellCommission = 0.0;
 
    _BuyComment = ""; _SellComment = ""; _BuyStopComment = ""; 
    _SellStopComment = ""; _BuyLimitComment = ""; 
_SellLimitComment = "";
 
    _BuyStopExpiration = -1; _SellStopExpiration = -1;
    _BuyLimitExpiration = -1; _SellLimitExpiration = -1;
 
    int _GetLastError = 0, _OrdersTotal = OrdersTotal();
    for ( int z = _OrdersTotal - 1; z >= 0; z -- )
    {
        if ( !OrderSelect( z, SELECT_BY_POS ) )
        {
          _GetLastError = GetLastError();
          Print("OrderSelect(", z, ",SELECT_BY_POS) - Error #", 
                _GetLastError );
          continue;
        }
        if ( OrderMagicNumber() == magic && OrderSymbol() == 
            Symbol() )
        {
          switch ( OrderType() )
          {
            case OP_BUY:
             _BuyTicket     = OrderTicket();
             _BuyLots       = NormalizeDouble( OrderLots(), 1 );
             _BuyOpenPrice  = NormalizeDouble( OrderOpenPrice(),
                                               Digits );
             _BuyStopLoss   = NormalizeDouble( OrderStopLoss(), 
                                               Digits );
             _BuyTakeProfit = NormalizeDouble( OrderTakeProfit(),
                                               Digits );
             _BuyOpenTime   = OrderOpenTime();
             _BuyProfit     = NormalizeDouble( OrderProfit(), 2 );
             _BuySwap       = NormalizeDouble( OrderSwap(), 2 );
             _BuyCommission = NormalizeDouble( OrderCommission(),
                                               2 );
             _BuyComment    = OrderComment();
             break;
           case OP_SELL:
             _SellTicket     = OrderTicket();
             _SellLots       = NormalizeDouble( OrderLots(), 1 );
             _SellOpenPrice  = NormalizeDouble( OrderOpenPrice(),
                                                Digits );
             _SellStopLoss   = NormalizeDouble( OrderStopLoss(),
                                                Digits );
             _SellTakeProfit = NormalizeDouble( OrderTakeProfit(),
                                                Digits );
             _SellOpenTime   = OrderOpenTime();
             _SellProfit     = NormalizeDouble( OrderProfit(), 2 );
             _SellSwap       = NormalizeDouble( OrderSwap(), 2 );
             _SellCommission = NormalizeDouble( OrderCommission(),
                                                2 );
             _SellComment    = OrderComment();
             break;
           case OP_BUYSTOP:
             _BuyStopTicket     = OrderTicket();
             _BuyStopLots       = NormalizeDouble( OrderLots(), 1 );
             _BuyStopOpenPrice  = NormalizeDouble( OrderOpenPrice(),
                                                   Digits );
             _BuyStopStopLoss   = NormalizeDouble( OrderStopLoss(),
                                                   Digits );
             _BuyStopTakeProfit = NormalizeDouble( OrderTakeProfit(),
                                                   Digits );
             _BuyStopOpenTime   = OrderOpenTime();
             _BuyStopComment    = OrderComment();
             _BuyStopExpiration = OrderExpiration();
             break;
          case OP_SELLSTOP:
             _SellStopTicket     = OrderTicket();
             _SellStopLots       = NormalizeDouble( OrderLots(), 1 );
             _SellStopOpenPrice  = NormalizeDouble( OrderOpenPrice(),
                                                    Digits );
             _SellStopStopLoss   = NormalizeDouble( OrderStopLoss(),
                                                    Digits );
             _SellStopTakeProfit = NormalizeDouble( OrderTakeProfit(),
                                                    Digits );
             _SellStopOpenTime   = OrderOpenTime();
             _SellStopComment    = OrderComment();
             _SellStopExpiration = OrderExpiration();
             break;
          case OP_BUYLIMIT:
             _BuyLimitTicket     = OrderTicket();
             _BuyLimitLots       = NormalizeDouble( OrderLots(), 1 );
             _BuyLimitOpenPrice  = NormalizeDouble( OrderOpenPrice(),
                                                    Digits );
             _BuyLimitStopLoss   = NormalizeDouble( OrderStopLoss(),
                                                    Digits );
             _BuyLimitTakeProfit = NormalizeDouble( OrderTakeProfit(),
                                                    Digits );
             _BuyLimitOpenTime   = OrderOpenTime();
             _BuyLimitComment    = OrderComment();
             _BuyLimitExpiration = OrderExpiration();
             break;
           case OP_SELLLIMIT:
             _SellLimitTicket     = OrderTicket();
             _SellLimitLots       = NormalizeDouble( OrderLots(), 1 );
             _SellLimitOpenPrice  = NormalizeDouble( OrderOpenPrice(),
                                                     Digits );
             _SellLimitStopLoss   = NormalizeDouble( OrderStopLoss(),
                                                     Digits );
             _SellLimitTakeProfit = NormalizeDouble( OrderTakeProfit(),
                                                     Digits );
             _SellLimitOpenTime   = OrderOpenTime();
             _SellLimitComment    = OrderComment();
             _SellLimitExpiration = OrderExpiration();
             break;
            }
        }
    }
}

Agora vamos anexar a função ao expert:


extern int    _MagicNumber = 1123;
 
extern double Lot          = 0.1;
extern int    StopLoss     = 60;    
// distance to StopLoss in points (0 - disable)
extern int    TakeProfit   = 100;   
// distance to TakeProfit in points (0 - disable)
extern int    TrailingStop = 50;    
// Trailing Stop in points (0 - disable)
 
extern int    Luft         = 20;    
// distance to the placing level of the pending order
 
#include <OneTypeOrdersControl.mq4>
 
int start()
{
    int _GetLastError = 0;
 
    //---- Memorize parameters of open positions (if available)
    OneTypeOrdersInit( _MagicNumber );
 
    //---- If we have both pending orders, quit, 
    //---- we have to wait until one of them triggers
    if ( _BuyStopTicket > 0 && _SellStopTicket > 0 ) return(0);
 
    //---- if there is an open BUY position,
    if ( _BuyTicket > 0 )
    {
        //---- if the SellStop has not been deleted yet, delete it:
        if ( _SellStopTicket > 0 )
        {
            if ( !OrderDelete( _SellStopTicket ) )
            {
                Alert( "OrderDelete Error #", GetLastError() );
                return(-1);
            }
        }
        //---- check whether the StopLoss should be moved:
        //---- if the Trailing Stop size is not too small,
        if ( TrailingStop > MarketInfo( Symbol(), 
             MODE_STOPLEVEL ) )
        {
//---- if the profit on the position exceeds TrailingStop points,
            if ( NormalizeDouble( Bid - _BuyOpenPrice, Digits ) > 
                  NormalizeDouble( TrailingStop*Point, Digits ) )
            {
//---- if the new level of StopLoss exceeds that of the current 
//     position
//---- (or if the position does not have a StopLoss),
                if(NormalizeDouble( Bid - TrailingStop*Point, 
                   Digits ) > _BuyStopLoss
                   || _BuyStopLoss <= 0.0 )
                {
                    //---- modify the order
                    if ( !OrderModify( _BuyTicket, _BuyOpenPrice, 
                          NormalizeDouble( Bid - TrailingStop*Point,
                          Digits ), 
                          _BuyTakeProfit, 0 ) )
                    {
                        Alert( "OrderModify Error #", 
                               GetLastError() );
                        return(-1);
                    }
                }                    
            }
        }
//---- if there is an open position, quit, there is nothing to do
        return(0);
    }
 
//---- The block below is absolutely similar to that processing 
//     the BUY position,
//---- this is why we do not comment on it...
    if ( _SellTicket > 0 )
    {
        if ( _BuyStopTicket > 0 )
        {
            if ( !OrderDelete( _BuyStopTicket ) )
            {
                Alert( "OrderDelete Error #", GetLastError() );
                return(-1);
            }
        }
        if(TrailingStop > MarketInfo( Symbol(), MODE_STOPLEVEL))
        {
            if(NormalizeDouble( _SellOpenPrice - Ask, Digits ) > 
                 NormalizeDouble( TrailingStop*Point, Digits ) )
            {
                if(NormalizeDouble( Ask + TrailingStop*Point, 
                   Digits ) < _SellStopLoss
                      || _SellStopLoss <= 0.0 )
                {
                    if(!OrderModify( _SellTicket, _SellOpenPrice, 
                       NormalizeDouble( Ask + TrailingStop*Point, 
                       Digits ), 
                       _SellTakeProfit, 0 ) )
                    {
                        Alert( "OrderModify Error #", 
                               GetLastError() );
                        return(-1);
                    }
                }                    
            }
        }
        return(0);
    }
 
 
//+------------------------------------------------------------------+
//| If execution has reached this point, this means that there are no|
//| pending orders or open positions                                 |
//+------------------------------------------------------------------+
//---- Place BuyStop and SellStop:
    double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel;
    _OpenPriceLevel = NormalizeDouble( Ask + Luft*Point, Digits);
 
    if ( StopLoss > 0 )
      _StopLossLevel = NormalizeDouble( _OpenPriceLevel - 
                       StopLoss*Point, Digits );
    else
      _StopLossLevel = 0.0;
 
    if ( TakeProfit > 0 )
      _TakeProfitLevel = NormalizeDouble( _OpenPriceLevel + 
                         TakeProfit*Point, Digits );
    else
      _TakeProfitLevel = 0.0;
 
    if(OrderSend ( Symbol(), OP_BUYSTOP, Lot, _OpenPriceLevel, 
       5, _StopLossLevel, _TakeProfitLevel, "", _MagicNumber ) < 0)
    {
        Alert( "OrderSend Error #", GetLastError() );
        return(-1);
    }
 
 
    _OpenPriceLevel = NormalizeDouble( Bid - Luft*Point, Digits);
 
    if ( StopLoss > 0 )
      _StopLossLevel = NormalizeDouble( _OpenPriceLevel + 
                       StopLoss*Point, Digits );
    else
      _StopLossLevel = 0.0;
 
    if ( TakeProfit > 0 )
      _TakeProfitLevel = NormalizeDouble( _OpenPriceLevel - 
                         TakeProfit*Point, Digits );
    else
      _TakeProfitLevel = 0.0;
 
    if(OrderSend ( Symbol(), OP_SELLSTOP, Lot, _OpenPriceLevel,
       5, _StopLossLevel, _TakeProfitLevel, "", 
       _MagicNumber ) < 0 )
    {
        Alert( "OrderSend Error #", GetLastError() );
        return(-1);
    }
 
    return(0);
}

Aqui a diferença entre o expert inicial e o revisado é muito mais perceptível – o bloco de controle de posições é de compreensão muito simples e fácil.

Agora é a vez dos experts mais complexos, aqueles que não possuem qualquer limitação em relação à quantidade de posições abertas simultaneamente.

4. Controle sobre todas as posições


O uso de variáveis foi suficiente para armazenar características de um pedido. Agora nós teremos que criar algumas matrizes, uma para cada característica. Com exceção deste fato, a função é praticamente igual:

  • "zeroizar" todas as matrizes durante a inicialização;
  • buscar em todos os pedidos e armazenar em matrizes apenas as características daqueles que possuam o símbolo necessário e o MagicNumber igual ao parâmetro da função 'mágica';
  • para melhorar a usabilidade, adicionar uma variável global que irá armazenar a contagem total de pedidos do expert – isso será útil durante o acesso das matrizes.

Vamos iniciar imediatamente a escrita da função:


// the variable that will store the amount of all orders of the expert:
int _ExpertOrdersTotal = 0;
 
// arrays where the order characteristics will be stored:
int _OrderTicket[], _OrderType[];
double _OrderLots[], _OrderOpenPrice[], _OrderStopLoss[], 
_OrderTakeProfit[];
double _OrderProfit[], _OrderSwap[], _OrderCommission[];
datetime _OrderOpenTime[], _OrderExpiration[];
string _OrderComment[];
 
void AllOrdersInit( int magic )
{
    int _GetLastError = 0, _OrdersTotal = OrdersTotal();
 
    // change array sizes according to the current amount of 
    // positions
    // (if _OrdersTotal = 0, change the arrays size for 1)
    int temp_value = MathMax( _OrdersTotal, 1 );
    ArrayResize( _OrderTicket,     temp_value );
    ArrayResize( _OrderType,       temp_value );
    ArrayResize( _OrderLots,       temp_value );
    ArrayResize( _OrderOpenPrice,  temp_value );
    ArrayResize( _OrderStopLoss,   temp_value );
    ArrayResize( _OrderTakeProfit, temp_value );
    ArrayResize( _OrderOpenTime,   temp_value );
    ArrayResize( _OrderProfit,     temp_value );
    ArrayResize( _OrderSwap,       temp_value );
    ArrayResize( _OrderCommission, temp_value );
    ArrayResize( _OrderComment,    temp_value );
    ArrayResize( _OrderExpiration, temp_value );
 
    // zeroize the arrays
    ArrayInitialize( _OrderTicket,     0 );
    ArrayInitialize( _OrderType,       0 );
    ArrayInitialize( _OrderLots,       0 );
    ArrayInitialize( _OrderOpenPrice,  0 );
    ArrayInitialize( _OrderStopLoss,   0 );
    ArrayInitialize( _OrderTakeProfit, 0 );
    ArrayInitialize( _OrderOpenTime,   0 );
    ArrayInitialize( _OrderProfit,     0 );
    ArrayInitialize( _OrderSwap,       0 );
    ArrayInitialize( _OrderCommission, 0 );
    ArrayInitialize( _OrderExpiration, 0 );
 
    _ExpertOrdersTotal = 0;
    for ( int z = _OrdersTotal - 1; z >= 0; z -- )
    {
        if ( !OrderSelect( z, SELECT_BY_POS ) )
        {
          _GetLastError = GetLastError();
          Print("OrderSelect(", z, ",SELECT_BY_POS) - Error #", 
                _GetLastError );
          continue;
        }
        if ( OrderMagicNumber() == magic && OrderSymbol() == 
             Symbol() )
        {
            // fill the arrays
            _OrderTicket[_ExpertOrdersTotal] = OrderTicket();
            _OrderType[_ExpertOrdersTotal] = OrderType();
            _OrderLots[_ExpertOrdersTotal] = 
            NormalizeDouble( OrderLots(), 1 );
            _OrderOpenPrice[_ExpertOrdersTotal] = 
            NormalizeDouble( OrderOpenPrice(), Digits );
            _OrderStopLoss[_ExpertOrdersTotal] = 
            NormalizeDouble( OrderStopLoss(), Digits );
            _OrderTakeProfit[_ExpertOrdersTotal] = 
            NormalizeDouble( OrderTakeProfit(), Digits );
            _OrderOpenTime[_ExpertOrdersTotal] = OrderOpenTime();
            _OrderProfit[_ExpertOrdersTotal] = 
            NormalizeDouble( OrderProfit(), 2 );
            _OrderSwap[_ExpertOrdersTotal] = 
            NormalizeDouble( OrderSwap(), 2 );
            _OrderCommission[_ExpertOrdersTotal] = 
            NormalizeDouble( OrderCommission(), 2 );
            _OrderComment[_ExpertOrdersTotal] = OrderComment();
            _OrderExpiration[_ExpertOrdersTotal] = 
            OrderExpiration();
            _ExpertOrdersTotal++;
        }
    }
 
    // change the arrays size according to the amount of 
    // positions that belong to the expert
    // (if _ExpertOrdersTotal = 0, change the arrays size for 1)
    temp_value = MathMax( _ExpertOrdersTotal, 1 );
    ArrayResize( _OrderTicket,     temp_value );
    ArrayResize( _OrderType,       temp_value );
    ArrayResize( _OrderLots,       temp_value );
    ArrayResize( _OrderOpenPrice,  temp_value );
    ArrayResize( _OrderStopLoss,   temp_value );
    ArrayResize( _OrderTakeProfit, temp_value );
    ArrayResize( _OrderOpenTime,   temp_value );
    ArrayResize( _OrderProfit,     temp_value );
    ArrayResize( _OrderSwap,       temp_value );
    ArrayResize( _OrderCommission, temp_value );
    ArrayResize( _OrderComment,    temp_value );
    ArrayResize( _OrderExpiration, temp_value );
}

Para examinar em detalhes o funcionamento da função, vamos escrever um expert simples capaz de exibir informações a respeito de todas as posições abertas pelo expert.

O seu código é bastante simples:

extern int _MagicNumber    = 0;
 
#include AllOrdersControl.mq4>
 
int start()
{
    AllOrdersInit( _MagicNumber );
 
    
    if ( _ExpertOrdersTotal > 0 )
    {
        string OrdersList = StringConcatenate(Symbol(), 
                   ", MagicNumber ", _MagicNumber, ":\n");
        for ( int n = 0; n _ExpertOrdersTotal; n ++ )
        {
            OrdersList = StringConcatenate( OrdersList, 
                            "Order # ", _OrderTicket[n], 
                ", profit/loss: ", 
                DoubleToStr( _OrderProfit[n], 2 ), 
                " ", AccountCurrency(), "\n" );            
        }
        Comment( OrdersList );
    }
 
    return(0);
}

Caso o _MagicNumber for configurado como 0, o expert irá exibir a lista de posições abertas manualmente:

5. Conclusão


Por fim, eu gostaria de comparar a velocidade dos experts que realizam buscas em seus pedidos de forma independente àquela dos experts que utilizam funções. Para este fim, ambas versões foram testadas no modo "Every tick" (a cada tick) 10 vezes consecutivamente (otimização pelo _MagicNumber). O tempo de teste foi medido pelo próprio MetaTrader – o tempo decorrido é medido automaticamente.

Os resultados são os seguintes:

Expert Advisor

Tempo decorrido em 10 tests (mm:ss)

CrossMACD_beta

(sem função)

07:42

CrossMACD

11:37

DoublePending_beta

(sem função)

08:18

DoublePending

09:42

Como você pode ver na tabela, experts que usam funções trabalham um pouco mais lentamente (na lista, durante testes). É um preço justo a se pagar pela usabilidade e simplicidade do código fonte do expert.


De todo modo, cada usuário deve decidir por si mesmo se irá usá-las ou não.