Скачать MetaTrader 5

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

2 июня 2006, 16:35
Andrey Khatimlianskii
18
7 298


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)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (18)
MQL4 Comments
MQL4 Comments | 3 апр 2007 в 02:05
как могу я получить от последних данных по заказа? не от сперва и 2-ое, только всегда получать последнюю информацию.

            case OP_BUY:
             _BuyTicket     = OrderTicket();
             _BuyLots       = NormalizeDouble( OrderLots(), 1 );
             _BuyOpenPrice  = NormalizeDouble( OrderOpenPrice(),
                                               Digits );
             _BuyStopLoss   = NormalizeDouble( OrderStopLoss(), 
                                               Digits );
             _BuyTakeProfit = NormalizeDouble( OrderTakeProfit(),
Andrey Khatimlianskii
Andrey Khatimlianskii | 3 апр 2007 в 02:32

egt520 писал(а):
как могу я получить от последних данных по заказа? не от сперва и 2-ое, только всегда получать последнюю информацию.

Замените эту строку
    for ( int z = _OrdersTotal - 1; z >= 0; z -- )
на эту:
    for ( int z = 0; z < _OrdersTotal; z ++ )
Sart
Sart | 4 авг 2007 в 23:33
Если Вас не затруднит, дайте ответ на такой вопрос, я думаю Вы продумывали эту тему.

Наши ордера появляются/удаляются на торговом сервере в нашем активном (ON_TRADE) пуле ордеров разными путями.
1) Немедленное исполние распоряжения на открытие ордера - в результате в пуле появляется ордер со всеми окончательно заполненными полями, за исключением поля OrderClosePrice. Остальные поля либо фиксированы, либо сопровождаются торговым сервером. Поле OrderOpenTime - отображает реальное время (время торговой платформы) открытия ордера. Здесь маленький вопрос-вероятно, он помещается в хвост пула, т.е. ему присваивается следующий по порядку номер ?
2) Установка отложенного ордера - в результате в пуле появляется ордер со всеми окончательно заполненными полями, за исключением поля OrderCloseTime. Здесь маленький вопрос OrderOpenTime - хранит ,скорее всего, время установки отложенного ордера ? И такой же вопрос как и в 1) - ему присваивается следующий по порядку номер в хвосте ?
3) При закрытие ордера (принудительно или по срабатыванию) происходит его удаление из активного пула и перевод в исторический пул(ON_HISTORY) - тут все ясно. И опять же тот самый вопрос-порядок следования ордеров в активном пуле остается прежним ? т.е., происходит простое поджатие ордеров, иди остается "дырка", а нумерация сохраняется прежней ?
4) При срабатывание отложенного ордера, что реально происходит в активном пуле-просто меняются значения полей в том же самом элементе активного пула ? а порядок и нумерация ордеров сохраняются ?

Короче вопросы такие:
- при добавление ордера в пул, он просто ставится в хвост пула, получая следующий в порядке возрастания номер(самый первый номер это нулевой) ?
- при вычеркивание/изменение(как в случае со срабатыванием отложеннонго ордера) как это отражается на порядке и нумерации ордеров в пуле ?

В Вашей функции AllOrderInit нулевые элементы массивов, представляют последний по номеру ордер в активном пуле. Что это за ордер -самый поздний по времени открытия/установки ?

С уважением - С.Д.
Andrey Khatimlianskii
Andrey Khatimlianskii | 8 авг 2007 в 15:18
Sart:
Если Вас не затруднит, дайте ответ на такой вопрос, я думаю Вы продумывали эту тему.
Вы могли бы сами проследить за появлением и исчезновением ордеров - это не сложно ;)

Sart:
- при добавление ордера в пул, он просто ставится в хвост пула, получая следующий в порядке возрастания номер(самый первый номер это нулевой) ?
Да

Sart:
- при вычеркивание/изменение(как в случае со срабатыванием отложеннонго ордера) как это отражается на порядке и нумерации ордеров в пуле ?
Закрытый/удаленный ордер перемещается в историю ("ON_HISTORY"), а все ордера, находящиеся в списке открытых ордеров "выше" закрытого, перемещаются "вниз".

Sart:
В Вашей функции AllOrderInit нулевые элементы массивов, представляют последний по номеру ордер в активном пуле. Что это за ордер -самый поздний по времени открытия/установки ?
Просто распечатайте время открытия и посмотрите сами ;)
Я, если честно, не проверял.

Кстати, возможно, порядок зависит от сортировки списка ордеров в терминале.
sergey
sergey | 26 сен 2009 в 01:12
спасибо помогло в изучении языка .. просто респект !!!!
Учёт ордеров в большой программе Учёт ордеров в большой программе

Рассматриваются общие принципы построения учёта ордеров в сложной программе.

Графический эксперт AutoGraf Графический эксперт AutoGraf

Рассматриваются возможности использования графических средств для создания удобного интерфейса управления торговлей.

Генетические алгоритмы - математический аппарат Генетические алгоритмы - математический аппарат

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

Графики без "дыр" Графики без "дыр"

Статья посвящена реализации графиков без пропущенных баров.