English 中文 Español Deutsch 日本語 Português
Управление ордерами – это просто

Управление ордерами – это просто

MetaTrader 4Трейдинг | 2 июня 2006, 16:35
18 574 22
Andrey Khatimlianskii
Andrey Khatimlianskii


1. Вступление

В каждом торгующем эксперте есть блок контроля открытых позиций. Это – перебор всех ордеров в цикле, выбор "своей" позиции по символу и и значению MagicNumber и последующее её изменение или закрытие. Выглядят такие блоки очень похоже, и, чаще всего, одинаковы по функциональности. Поэтому можно вынести этот повторяющийся кусок кода из эксперта в функцию – это значительно упростит написание экспертов и сделает код экспертов компактнее.

Для начала, давайте, разделим задачу на три этапа, отличающихся по сложности и функциональности – они будут соответствовать трем видам экспертов:

  • Эксперты, у которых одновременно может быть открыта только одна позиция;
  • Эксперты, у которых может быть открыто по одной позиции каждого типа (например, одна позиция buy и одна позиция sell);
  • Эксперты, у которых может быть открыто любое количество позиций.


2. Одна позиция

Существует много стратегий, которые используют только одну открытую позицию. Их блоки контроля позиций выглядят достаточно просто, но, тем не менее, их написание занимает время и требует внимания.

Давайте возьмем простого эксперта, сигналом для открытия позиции у которого, служит пересечение линий MACD, сигнальной и основной, и упростим его блок контроля позиций. Вот, как он выглядит до доработки:

extern int  _MagicNumber=1122;

int start()
  {
//---- Запоминаем значения индикатора для дальнейшего анализа
   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();
//---- перебираем все открытые позиции
   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(OrderSymbol()!=Symbol()) continue;

      // если MagicNumber не равен _MagicNumber, пропускаем 
      // эту позицию
      if(OrderMagicNumber()!=_MagicNumber) continue;

      //---- если открыта БАЙ-позиция,
      if(OrderType()==OP_BUY)
        {
         //---- если МАКД пересёк 0-ю линию вниз,
         if(NormalizeDouble(MACD_1,Digits+1)<0.0 && 
            NormalizeDouble(MACD_2,Digits+1)>=0.0)
           {
            //---- закрываем позицию
            if(!OrderClose(OrderTicket(),OrderLots(),
               Bid,5,Green))
              {
               _GetLastError=GetLastError();
               Alert("Ошибка OrderClose № ",_GetLastError);
               return(-1);
              }
           }
         // если сигнал не изменился, выходим - пока рано открывать 
         // новую позицию
         else
           { return(0); }
        }
      //---- если открыта СЕЛЛ-позиция,
      if(OrderType()==OP_SELL)
        {
         //---- если МАКД пересёк 0-ю линию вверх,
         if(NormalizeDouble(MACD_1, Digits+1)>  0.0 && 
            NormalizeDouble(MACD_2,Digits+1)<=0.0)
           {
            //---- закрываем позицию
            if(!OrderClose(OrderTicket(),OrderLots(),
               Ask,5,Red))
              {
               _GetLastError=GetLastError();
               Alert("Ошибка OrderClose № ",_GetLastError);
               return(-1);
              }
           }
         // если сигнал не изменился, выходим - пока рано открывать 
         // новую позицию
         else return(0);
        }
     }

//+------------------------------------------------------------------+
//| если выполнение дошло до этого места, значит открытой позиции нет|
//| проверяем, есть ли возможность открыть позицию                   |
//+------------------------------------------------------------------+

//---- если МАКД пересёк 0-ю линию вверх,
   if(NormalizeDouble(MACD_1,Digits+1)>0.0 && 
      NormalizeDouble(MACD_2,Digits+1)<=0.0)
     {
      //---- открываем БАЙ позицию
      if(OrderSend(Symbol(),OP_BUY,0.1,Ask,5,0.0,
         0.0,"MACD_test",
         _MagicNumber,0,Green)<0)
        {
         _GetLastError=GetLastError();
         Alert("Ошибка OrderSend № ",_GetLastError);
         return(-1);
        }
      return(0);
     }
//---- если МАКД пересёк 0-ю линию вниз,
   if(NormalizeDouble(MACD_1,Digits+1)<0.0 && 
      NormalizeDouble(MACD_2,Digits+1)>=0.0)
     {
      //---- открываем СЕЛЛ позицию
      if(OrderSend(Symbol(),OP_SELL,0.1,Bid,5,0.0,0.0,"MACD_test",
         _MagicNumber,0,Red)<0)
        {
         _GetLastError=GetLastError();
         Alert("Ошибка OrderSend № ",_GetLastError);
         return(-1);
        }
      return(0);
     }

   return(0);
  }

Теперь нам необходимо написать функцию, которая заменит блок контроля позиций. Функция должна перебирать все ордера, находить нужный и запоминать все его характеристики в глобальные переменные. Она будет выглядеть так

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;
        }
    }
}

Как видите, всё просто: есть 11 переменных, каждая хранит значение одной характеристики позиции (№ тикета, тип, размер лота, и т.д.). В начале функции происходит обнуление этих переменных. Это необходимо потому, что они объявлены на глобальном уровне и не обнуляются при вызове функции, а нам не нужна информация с предыдущего тика – все данные должны быть свежими. Потом идёт стандартный перебор всех открытых позиций и, в случае совпадения символа и значения MagicNumber, запоминание характеристик в соответствующие переменные.

Теперь давайте подключим эту функцию к нашему эксперту:

extern int  _MagicNumber = 1122;
 
#include <OneOrderControl.mq4>
 
int start()
{
    int _GetLastError = 0;
    
    //---- Запоминаем параметры открытой позиции (если она есть)
    OneOrderInit( _MagicNumber );
 
    //---- Запоминаем значения индикатора для дальнейшего анализа
    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 );
 
// А теперь, вместо перебора позиций, просто смотрим, есть ли 
// открытая позиция:
    if ( _Ticket > 0 )
    {
        //---- если открыта БАЙ-позиция,
        if ( _Type == OP_BUY )
        {
            //---- если МАКД пересёк 0-ю линию вниз,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
            {
                //---- закрываем позицию
                if ( !OrderClose( _Ticket, _Lots, Bid, 5, Green ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Ошибка OrderClose № ", _GetLastError );
                    return(-1);
                }
            }
// если сигнал не изменился, выходим - пока рано открывать 
// новую позицию
            else return(0);
        }
        //---- если открыта СЕЛЛ-позиция,
        if ( _Type == OP_SELL )
        {
            //---- если МАКД пересёк 0-ю линию вверх,
            if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
                  NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
            {
                //---- закрываем позицию
                if ( !OrderClose( _Ticket, _Lots, Ask, 5, Red ) )
                {
                    _GetLastError = GetLastError();
                    Alert( "Ошибка OrderClose № ", _GetLastError );
                    return(-1);
                }
            }
// если сигнал не изменился, выходим - пока рано открывать 
// новую позицию
            else return(0);
        }
    }
    //---- если нет позиции, открытой экспертом ( _Ticket == 0 )
    //---- если МАКД пересёк 0-ю линию вверх,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) >  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) <= 0.0    )
    {
        //---- открываем БАЙ позицию
        if(OrderSend( Symbol(), OP_BUY, 0.1, Ask, 5, 0.0, 0.0, 
           "CrossMACD", _MagicNumber, 0, Green ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Ошибка OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }
    //---- если МАКД пересёк 0-ю линию вниз,
    if ( NormalizeDouble( MACD_1, Digits + 1 ) <  0.0 && 
          NormalizeDouble( MACD_2, Digits + 1 ) >= 0.0    )
    {
        //---- открываем СЕЛЛ позицию
        if(OrderSend( Symbol(), OP_SELL, 0.1, Bid, 5, 0.0, 
           0.0, "CrossMACD", _MagicNumber, 0, Red ) < 0 )
        {
            _GetLastError = GetLastError();
            Alert( "Ошибка OrderSend № ", _GetLastError );
            return(-1);
        }
        return(0);
    }

    return(0);
}

Как видите, теперь код эксперта намного компактнее и удобнее для восприятия. И это – самый простой случай.

Давайте теперь разберёмся со следующей задачей.

 

3. Одна позиция одного типа

Для реализации следующей функции нам нужен эксперт посложнее. Он должен открывать несколько позиций разного типа, и потом что-нибудь с ними делать. Давайте попробуем в двух словах сформулировать алгоритм эксперта:

  • При запуске эксперт должен устанавливать два отложенных ордера: бай-стоп на уровне Аск + 20 пунктов и селл-стоп на уровне Бид – 20 пунктов;
  • При срабатывании одного из ордеров второй должен быть удален;
  • Открытая позиция должна сопровождаться Трейлинг Стопом;
  • После закрытия позиции по Стоп Лоссу или Тейк Профиту переходим к пункту первому, то есть опять устанавливаем два отложенных ордера.

Код эксперта будет выглядеть так:

extern int    _MagicNumber = 1123;
 
extern double Lot          = 0.1;
extern int    StopLoss     = 60;    
// расстояние до СтопЛосса в пунктах (0 - отключить СЛ)
extern int    TakeProfit   = 100;   
// расстояние до ТейкПрофита в пунктах (0 - отключить)
extern int    TrailingStop = 50;    
// размер ТрейлингСтопа в пунктах (0 - отключить)
 
extern int    Luft         = 20;    
// расстояние до уровня установки отложенного ордера
 
 
int start()
{
// Переменные, в которые будем запоминать тикеты ордеров каждого типа
    int BuyStopOrder = 0, SellStopOrder = 0, BuyOrder = 0, SellOrder = 0;
    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 ( OrderSymbol() != Symbol() ) continue;
 
// если MagicNumber не равен _MagicNumber, пропускаем эту позицию
        if ( OrderMagicNumber() != _MagicNumber ) continue;
 
// в зависимости от типа позиции, меняем значение переменной:
        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 ( BuyStopOrder > 0 && SellStopOrder > 0 ) return(0);
 
// перебираем все открытые позиции во второй раз - теперь 
// мы будем с ними работать:
    _OrdersTotal = OrdersTotal();
    for ( z = _OrdersTotal - 1; z >= 0; z -- )
    {
// если при выборе позиции возникла ошибка, переходим к 
// следующей
        if ( !OrderSelect( z, SELECT_BY_POS ) )
        {
          _GetLastError = GetLastError();
          Print("OrderSelect( ", z, ", SELECT_BY_POS ) - Error #", 
                _GetLastError );
          continue;
        }
 
// если позиция открыта не по текущему инструменту, пропускаем её
        if ( OrderSymbol() != Symbol() ) continue;
 
// если MagicNumber не равен _MagicNumber, пропускаем эту позицию
        if ( OrderMagicNumber() != _MagicNumber ) continue;
 
// в зависимости от типа позиции, меняем значение переменной:
        switch ( OrderType() )
        {
            //---- если есть открытая бай-позиция,
            case OP_BUY:
            {
                //---- если селл-стоп ордер ещё не удалён, удаляем его:
                if ( SellStopOrder > 0 )
                {
                    if ( !OrderDelete( SellStopOrder ) )
                    {
                        Alert( "OrderDelete Error #", GetLastError() );
                        return(-1);
                    }
                }
                //---- проверяем, не надо ли передвинуть Стоп Лосс:
                //---- если размер трейлингстопа не слишком маленький,
                if(TrailingStop > MarketInfo( Symbol(), MODE_STOPLEVEL))
                {
                  //---- если прибыль позиции больше TrailingStop пунктов,
                  if(NormalizeDouble( Bid - OrderOpenPrice(), Digits) > 
                     NormalizeDouble( TrailingStop*Point, Digits))
                    {
                        // если новый уровень стоплосса выше, 
                        // чем сейчас у позиции
                        // (или если у позиции нет Стоп Лосса),
                        if(NormalizeDouble(Bid - TrailingStop*Point, 
                           Digits) > OrderStopLoss()
                           || OrderStopLoss() <= 0.0 )
                        {
                          //---- модифицируем ордер
                          if(!OrderModify( OrderTicket(), 
                             OrderOpenPrice(), 
                             NormalizeDouble(Bid - 
                             TrailingStop*Point,Digits), 
                             OrderTakeProfit(), 
                             OrderExpiration() ) )
                            {
                              Alert("OrderModify Error #", 
                                    GetLastError());
                              return(-1);
                            }
                        }                    
                    }
                }
                // если есть открытая позиция, выходим - 
                // дальше делать нечего
                return(0);
            }
            // Следующий блок полностью аналогичен блоку 
            // обработки бай-позиции,
            // поэтому комментарии к нему не присведены...
            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);
            }
        }
    }
 
//+------------------------------------------------------------------+
//| Если выполнение дошло до этого места, значит отложенных ордеров  |
//| и открытых позиций нет                                           |
//+------------------------------------------------------------------+
//---- Устанавливаем бай-стоп и селл-стоп ордера:
    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);
}

Теперь давайте напишем функцию, которая упростит блок контроля открытых позиций. Она должна находить по одному ордеру каждого типа, и сохранять его характеристики в глобальные переменные. Выглядеть она будет так:

// глобальные переменные, в которых будут хранитсья характеристики 
// ордеров:
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 )
{
// обнуление переменных:
    _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;
            }
        }
    }
}

А теперь подключим функцию к эксперту:

extern int    _MagicNumber = 1123;
 
extern double Lot          = 0.1;
extern int    StopLoss     = 60;    
// расстояние до СтопЛосса в пунктах (0 - отключить СЛ)
extern int    TakeProfit   = 100;   
// расстояние до ТейкПрофита в пунктах (0 - отключить)
extern int    TrailingStop = 50;    
// размер ТрейлингСтопа в пунктах (0 - отключить)
 
extern int    Luft         = 20;    
// расстояние до уровня установки отложенного ордера
 
#include <OneTypeOrdersControl.mq4>
 
int start()
{
    int _GetLastError = 0;
 
    //---- Запоминаем параметры открытых позиций (если они есть)
    OneTypeOrdersInit( _MagicNumber );
 
    //---- Если у нас есть оба отложенных ордера - выходим, 
    //---- надо подождать, пока один из них сработает
    if ( _BuyStopTicket > 0 && _SellStopTicket > 0 ) return(0);
 
    //---- если есть открытая бай-позиция,
    if ( _BuyTicket > 0 )
    {
        //---- если селл-стоп ордер ещё не удалён, удаляем его:
        if ( _SellStopTicket > 0 )
        {
            if ( !OrderDelete( _SellStopTicket ) )
            {
                Alert( "OrderDelete Error #", GetLastError() );
                return(-1);
            }
        }
        //---- проверяем, не надо ли передвинуть Стоп Лосс:
        //---- если размер трейлингстопа не слишком маленький,
        if ( TrailingStop > MarketInfo( Symbol(), MODE_STOPLEVEL ) )
        {
            //---- если прибыль позиции больше TrailingStop пунктов,
            if ( NormalizeDouble( Bid - _BuyOpenPrice, Digits ) > 
                  NormalizeDouble( TrailingStop*Point, Digits ) )
            {
                //---- если новый уровень стоплосса выше, чем сейчас у позиции
                //---- (или если у позиции нет Стоп Лосса),
                if ( NormalizeDouble( Bid - TrailingStop*Point, 
                    Digits ) > _BuyStopLoss
                      || _BuyStopLoss <= 0.0 )
                {
                    //---- модифицируем ордер
                    if ( !OrderModify( _BuyTicket, _BuyOpenPrice, 
                          NormalizeDouble( Bid - TrailingStop*Point, 
                                           Digits ), 
                          _BuyTakeProfit, 0 ) )
                    {
                        Alert( "OrderModify Error #", 
                              GetLastError() );
                        return(-1);
                    }
                }                    
            }
        }
 // если есть открытая позиция, выходим - дальше делать нечего
        return(0);
    }
 
// Следующий блок полностью аналогичен блоку обработки бай-позиции,
// поэтому комментарии к нему не присведены...
    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);
    }
 
 
//+------------------------------------------------------------------+
//| Если выполнение дошло до этого места, значит отложенных ордеров  |
//| и открытых позиций нет                                           |
//+------------------------------------------------------------------+
//---- Устанавливаем бай-стоп и селл-стоп ордера:
    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);
}

Здесь разница между исходным и переделанным экспертами намного заметнее – блок контроля позиций очень прост и понятен.

Теперь очередь самых сложных экспертов – экспертов, у которых нет ограничения на количество открытых позиций.

 

4. Контроль над всеми позициями

Если для хранения характеристик одного ордера можно было использовать переменные, то сейчас нам придется заводить несколько массивов – по одному на каждую характеристику. А в остальном смысл функции очень похож:

  • При старте, обнуляем все массивы;
  • Потом перебираем все ордера, и записываем в массивы характеристики только тех, у которых символ соответствует символу графика, и MagicNumber равен параметру функции magic;
  • Для удобства использования, добавим одну глобальную переменную, которая будет хранить общее количество ордеров, принадлежащих эксперту – при обращении к массивам это будет очень кстати.

Сразу приступаем к написанию функции:

// переменная, которая будет хранить количество ордеров, 
// принадлежащих эксперту:
int _ExpertOrdersTotal=0;

// массивы, в которых будут хранитсья характеристики ордеров:
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();

// изменяем размеры массивов под текущее кол-во позиций
// (если _OrdersTotal = 0, меняем размер массивов на 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);

// обнуляем массивы
   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())
        {
         // заполняем массивы
         _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++;
        }
     }
// изменяем размеры массивов под кол-во позиций, принадлежащих эксперту
// (если _ExpertOrdersTotal = 0, меняем размер массивов на 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);
  }

Чтоб понять принцип работы функции, давайте напишем простого эксперта, задачей которого будет вывод информации о всех позициях, открытых экспертом, на экран.

Его код достаточно простой:

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,"Ордер № ",_OrderTicket[n],", прибыль/убыток: ",DoubleToStr(_OrderProfit[n],2)," ",AccountCurrency(),"\n");
        }
      Comment(OrdersList);
     }

   return(0);
  }

Если установить _MagicNumber равным 0, эксперт будет отображать список позиций, открытых вручную:

 

5. Заключение

В заключительной части статьи хотелось бы сравнить быстродействие экспертов, самостоятельно перебирающих свои ордера, и экспертов, использующих функции. Для этого были протестированы обе версии в режиме "все тики" 10 раз подряд (оптимизация по параметру _MagicNumber). Замер времени тестирования проводил сам Meta Trader – при оптимизации автоматически считается затраченное время.

Итак, итоги:

Эксперт

Время, затраченное на 10 тестов (мм:сс)

CrossMACD_beta (без функции)

07:42

CrossMACD

11:37

DoublePending_beta (без функции)

08:18

DoublePending

09:42

Как видно из таблицы, советники, использующие функции, работают медленнее (по крайней мере, в режиме тестирования). Но это - небольшая плата за удобство написания и простоту исходного кода эксперта.

В любом случае, использовать их или нет – решать каждому самостоятельно.


Прикрепленные файлы |
CrossMACD.mq4 (3.01 KB)
CrossMACD_beta.mq4 (3.63 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (22)
sergey
sergey | 26 сент. 2009 в 01:12
спасибо помогло в изучении языка .. просто респект !!!!
Arseniy Suharev
Arseniy Suharev | 1 мая 2021 в 13:23

event handling function not found.

Сможете исправить, или уже код устарел?


Andrey Khatimlianskii
Andrey Khatimlianskii | 3 мая 2021 в 18:40
Arseniy Suharev:

event handling function not found.

Сможете исправить, или уже код устарел?


Устарел.

Но можно заменить

int start()

на

void OnTick()

и должно взлететь.

Arseniy Suharev
Arseniy Suharev | 5 мая 2021 в 05:15
Андрей, можете подсказать функцию, чтобы открытые ордера закрывались через 1,2 или 3 бара например. Использовал Fun_New_Bar, как не мудрил ордера закрываются на первом новом баре и после закрытого ордера на новом баре уже не открываются уже не открывается новый ордер. Хотел все это дело привязать к вашему советнику и в итоге оказался в лесу.
Andrey Khatimlianskii
Andrey Khatimlianskii | 5 мая 2021 в 20:26
Arseniy Suharev:
Андрей, можете подсказать функцию, чтобы открытые ордера закрывались через 1,2 или 3 бара например. Использовал Fun_New_Bar, как не мудрил ордера закрываются на первом новом баре и после закрытого ордера на новом баре уже не открываются уже не открывается новый ордер. Хотел все это дело привязать к вашему советнику и в итоге оказался в лесу.
                        for ( int z = OrdersTotal()-1; z >= 0; z -- )
                        {
                                if ( !OrderSelect( z, SELECT_BY_POS, MODE_TRADES ) )
                                {
                                        Print( "OrderSelect( ", z, ", SELECT_BY_POS, MODE_TRADES ) - Error #", GetLastError() );
                                        continue;
                                }
                                if ( OrderMagicNumber() != MagicNumber ) ) continue;
                                if ( OrderSymbol() != _Symbol ) continue;
                
                                if ( OrderType() == OP_BUY || OrderType() == OP_SELL )
                                {
                                        if ( iBarShift( _Symbol, PERIOD_CURRENT, OrderOpenTime(), false ) >= 3 ) OrderClose( OrderTicket() );
                                }
                        }
Генетические алгоритмы - математический аппарат Генетические алгоритмы - математический аппарат
Генетические алгоритмы предназначены для решения задач оптимизации. Примером подобной задачи может служить обучение нейросети, то есть подбора таких значений весов, при которых достигается минимальная ошибка. При этом в основе генетического алгоритма лежит метод случайного поиска.
Учёт ордеров в большой программе Учёт ордеров в большой программе
Рассматриваются общие принципы построения учёта ордеров в сложной программе.
Графики без "дыр" Графики без "дыр"
Статья посвящена реализации графиков без пропущенных баров.
Графический эксперт AutoGraf Графический эксперт AutoGraf
Рассматриваются возможности использования графических средств для создания удобного интерфейса управления торговлей.