| / | Статьи |
Cтатьи
Примеры
Управление ордерами – это просто
Авторизуйтесь или зарегистрируйтесь , чтобы добавить новую статью
|
Управление ордерами – это просто [ en | cn ]1. ВступлениеВ каждом торгующем эксперте есть блок контроля открытых позиций. Это – перебор всех ордеров в цикле, выбор "своей" позиции по символу и и значению MagicNumber и последующее её изменение или закрытие. Выглядят такие блоки очень похоже, и, чаще всего, одинаковы по функциональности. Поэтому можно вынести этот повторяющийся кусок кода из эксперта в функцию – это значительно упростит написание экспертов и сделает код экспертов компактнее. Для начала, давайте, разделим задачу на три этапа, отличающихся по сложности и функциональности – они будут соответствовать трем видам экспертов:
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. Одна позиция одного типаДля реализации следующей функции нам нужен эксперт посложнее. Он должен открывать несколько позиций разного типа, и потом что-нибудь с ними делать. Давайте попробуем в двух словах сформулировать алгоритм эксперта:
Код эксперта будет выглядеть так: 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. Контроль над всеми позициямиЕсли для хранения характеристик одного ордера можно было использовать переменные, то сейчас нам придется заводить несколько массивов – по одному на каждую характеристику. А в остальном смысл функции очень похож:
Сразу приступаем к написанию функции: // переменная, которая будет хранить количество ордеров, // принадлежащих эксперту: 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 – при оптимизации автоматически считается затраченное время. Итак, итоги:
Как видно из таблицы, советники, использующие функции, работают медленнее (по крайней мере, в режиме тестирования). Но это - небольшая плата за удобство написания и простоту исходного кода эксперта. В любом случае, использовать их или нет – решать каждому самостоятельно. Прикрепленные файлы:
Предупреждение:
все права на данные материалы
принадлежат MetaQuotes Software Corp. Полная или частичная перепечатка запрещена.
Sart писал(а): Вы могли бы сами проследить за появлением и исчезновением ордеров
- это не сложно ;)Если Вас не затруднит, дайте ответ на такой вопрос, я думаю Вы продумывали эту тему. Sart писал(а): Да- при добавление ордера в пул, он просто ставится в хвост пула, получая следующий в порядке возрастания номер(самый первый номер это нулевой) ? Sart писал(а): Закрытый/удаленный ордер перемещается в историю ("ON_HISTORY"),
а все ордера, находящиеся в списке открытых ордеров "выше"
закрытого, перемещаются "вниз".- при вычеркивание/изменение(как в случае со срабатыванием отложеннонго ордера) как это отражается на порядке и нумерации ордеров в пуле ? Sart писал(а): Просто распечатайте время открытия и посмотрите сами ;)В Вашей функции AllOrderInit нулевые элементы массивов, представляют последний по номеру ордер в активном пуле. Что это за ордер -самый поздний по времени открытия/установки ? Я, если честно, не проверял. Кстати, возможно, порядок зависит от сортировки списка ордеров в терминале.
08.08.2007 17:18 komposter
Если Вас не затруднит, дайте ответ на такой вопрос, я думаю Вы
продумывали эту тему.
Наши ордера появляются/удаляются на торговом сервере в нашем активном (ON_TRADE) пуле ордеров разными путями. 1) Немедленное исполние распоряжения на открытие ордера - в результате в пуле появляется ордер со всеми окончательно заполненными полями, за исключением поля OrderClosePrice. Остальные поля либо фиксированы, либо сопровождаются торговым сервером. Поле OrderOpenTime - отображает реальное время (время торговой платформы) открытия ордера. Здесь маленький вопрос-вероятно, он помещается в хвост пула, т.е. ему присваивается следующий по порядку номер ? 2) Установка отложенного ордера - в результате в пуле появляется ордер со всеми окончательно заполненными полями, за исключением поля OrderCloseTime. Здесь маленький вопрос OrderOpenTime - хранит ,скорее всего, время установки отложенного ордера ? И такой же вопрос как и в 1) - ему присваивается следующий по порядку номер в хвосте ? 3) При закрытие ордера (принудительно или по срабатыванию) происходит его удаление из активного пула и перевод в исторический пул(ON_HISTORY) - тут все ясно. И опять же тот самый вопрос-порядок следования ордеров в активном пуле остается прежним ? т.е., происходит простое поджатие ордеров, иди остается "дырка", а нумерация сохраняется прежней ? 4) При срабатывание отложенного ордера, что реально происходит в активном пуле-просто меняются значения полей в том же самом элементе активного пула ? а порядок и нумерация ордеров сохраняются ? Короче вопросы такие: - при добавление ордера в пул, он просто ставится в хвост пула, получая следующий в порядке возрастания номер(самый первый номер это нулевой) ? - при вычеркивание/изменение(как в случае со срабатыванием отложеннонго ордера) как это отражается на порядке и нумерации ордеров в пуле ? В Вашей функции AllOrderInit нулевые элементы массивов, представляют последний по номеру ордер в активном пуле. Что это за ордер -самый поздний по времени открытия/установки ? С уважением - С.Д.
05.08.2007 01:33 Sart
egt520 писал(а): for ( int z = _OrdersTotal - 1; z >= 0; z -- )на эту: for ( int z = 0; z < _OrdersTotal; z ++ )
03.04.2007 04:32 komposter
как могу я получить от последних данных по заказа? не от сперва
и 2-ое, только всегда получать последнюю информацию.
case OP_BUY: _BuyTicket = OrderTicket(); _BuyLots = NormalizeDouble( OrderLots(), 1 ); _BuyOpenPrice = NormalizeDouble( OrderOpenPrice(), Digits ); _BuyStopLoss = NormalizeDouble( OrderStopLoss(), Digits ); _BuyTakeProfit = NormalizeDouble( OrderTakeProfit(),
03.04.2007 04:05 egt520
komposter писал(а): Спасибо за разъяснения.Если в эксперте 2 независимых стратегии, проще 2 раза использовать
функцию OneTypeOrdersInit с разными мейджиками. Это будет и проще, и компактнее. Единственный недостаток - чуть медленнее. Потому что, используя массивы, всё равно придется различать ордера одной стратегии от другой, а это - дополнительное усложнение кода. Да, я так именно и делал в тех экспертах где у меня более 2-х независимых тактик и групп ордеров, и все они управляемы семафором. Но код в данном случае получается грамоздким, каждый магик со своими функциями, а это очень много. Я понимаю, что трудно давать какие либо советы, не зная конкретного алгоритма советника. Но когда я закончу излагать на одном из форумов тему, где будет расписано все в деталях и выложен код, я вас приглашу заглянуть, если у вас выкроется время, то буду благодарен вашей помощи. С уважением, Вячеслав.
19.12.2006 09:18 Vyacheslav
Vyacheslav писал(а): Если в эксперте 2 независимых стратегии, проще 2 раза использовать
функцию OneTypeOrdersInit с разными мейджиками.Именно так и есть, логика советника довольно сложная из двух тактик независимых друг от друга Это будет и проще, и компактнее. Единственный недостаток - чуть медленнее. Потому что, используя массивы, всё равно придется различать ордера одной стратегии от другой, а это - дополнительное усложнение кода.
18.12.2006 17:59 komposter
komposter писал(а): Cпасбио за ответ. Не совсем понимаю, в чем проблема... Использовать функцию есть смысл только если логика эксперта достаточно сложная, и в течении одного выполнения функции start() необходимо получать значения параметров ордеров много раз. Иначе проще (и быстрее) использовать обычный блок контроля позиций. Если у вас возникают трудности в написании экспертов, обратитесь к опытному программисту. Мои координаты есть в моем профиле, других разработчиков вы можете найти на многочисленых форумах (в том числе на этом). Удачи ;) Именно так и есть, логика советника довольно сложная из двух тактик независимых друг от друга поэтому и обратился к вам за помощью, поскольку я не являюсь профессиональным программистом, то многие коды сложные коды мне достаточно только увидеть на простом примере и в дальнейшем я уже сам потихоньку сделаю. В своем первом варианте где я вынужден был использовать три функции OneTypeOrdersInit с различными магиками при наличиии семафора у меня получился, но код оказался громоздким. Поэтому в своем самообучении при написании именно сложных советников у меня есть временные затруднения, которые я непременно со временем разрешу. Работа с функцией массивов у меня тоже пошла, но не в оригинальном виде, как у вас, а пришлось немного видоизменить. Вот почему я и обратился к вам и попросил пример, которого мне будет достаточно увидеть, чтобы понять где у меня ошибка. Для меня важно один раз увидеть, чем семь раз услышать, сами понимаете без базового образования приходится все постигать методом проб и ошибок. С уважением, Вячеслав.
18.12.2006 17:36 Vyacheslav
Vyacheslav писал(а): Не совсем понимаю, в чем проблема...Нельзя ли показать работу последней функции с массивами на примере
вашего же советника, использованного для примера в данной статье,
предположив что вместо 2 отложенных ордеров необходимо выставить
4 или более, но на других уровнях. Эксперт с использованием этой функции будет очень похож на обычный, только вместо перебора ордеров "от 0 до OrdersTotal()-1" будет перебор ордеров "от 0 до _ExpertOrdersTotal-1". Значение параметров ордеров во втором случае будут получаться не с помощью функций OrderType(), OrderLots() и подобных, а напрямую из массивов _OrderType[], _OrderLots[], и т.д. Использовать функцию есть смысл только если логика эксперта достаточно сложная, и в течении одного выполнения функции start() необходимо получать значения параметров ордеров много раз. Иначе проще (и быстрее) использовать обычный блок контроля позиций. Если у вас возникают трудности в написании экспертов, обратитесь к опытному программисту. Мои координаты есть в моем профиле, других разработчиков вы можете найти на многочисленых форумах (в том числе на этом). Удачи ;)
17.12.2006 14:29 komposter
Просьба к автору!
14.12.2006 10:04 Vyacheslav
18 комментариев: 1 2
|