TakeProfit (и StopLoss) закрытой позиции

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

Эта особенность очень хорошо видна в тестере, когда срабатывает TP: появляется соответствующий маркет-ордер (а не limit). Поскольку takeprofit - это не существующий биржевой вид ордеров (есть только маркеты и лимитные), то такое положение вещей вполне укладывалось в "рыночность".

С появлением хэджа ситуация не изменилась - takeprofit остались виртуальными. При этом в MT4 ситуация иная: за многие годы существования MT4-мостов стало стандартом, что MT4-takeprofit - это лимитные ордера.

Именно из-за этой виртуальности, как шлейф, тянутся некоторые особенности MT5. Мало того, что takeprofit в MT5 лучше не выставлять, если вы хотите лимитный аналог, то после закрытия позиции вы никак через MQL5 не сможете узнать ее значения takeprofit и stoploss.

Это не огульные обвинения, а результат многочасовых попыток разобраться (никакие HistorySelectByPosition и прочее не помогают), как же все работает. И я готов с радостью принести свои извинения, если ошибаюсь. Чтобы не быть голословным привожу советник для тестера (так проще разобраться) на сервере RoboForexEU-MetaTrader 5, который открывает позицию, затем ставит SL и TP-уровни.

void OnTick()
{
  static bool Flag = true;

  if (Flag)
  {
    // Открываем SELL-позицию
    MqlTradeRequest Request = {0};

    Request.action = TRADE_ACTION_DEAL;

    Request.symbol = Symbol();
    Request.volume = 1;
    Request.price = SymbolInfoDouble(Symbol(), SYMBOL_BID);

    Request.type = ORDER_TYPE_SELL;

    MqlTradeResult Result;

    if (OrderSend(Request, Result))
    {
      // Устанавливаем SL и TP
      Request.position = Result.deal;

      Request.action = TRADE_ACTION_SLTP;

      Request.tp = Result.ask - 10 * _Point;
      Request.sl = Result.ask + 10 * _Point;

      if (OrderSend(Request, Result))
        Print("Сделка в тестере закроется либо по SL, TP, либо по окончании бэктеста")    ;

      Flag = false;
    }
  }
}

В этом советнике невозможно определить (в OnDeinit) SL и TP единственной закрытой позиции. Это так задумано?

 

После прочтения тонн букв с форума поиском все же удалось найти ответ Рената

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Поддержка хеджирующего МТ5

Renat Fatkhullin, 2016.06.10 11:21

1. Дата удаления есть в разрезе "Ордера"

2. Это определяется не по тикетам, а только по дате закрытия как в МТ4, так и в МТ5.

3. Пока это можно извлечь только из MQL5, но постараемся тоже отобразить в истории

4. Пока это можно извлечь только из MQL5, но постараемся тоже отобразить в истории или как минимум в тултипах показывать будем

Будет в следующем билде.

Как осуществить третий пункт?! 

 
fxsaber:

После прочтения тонн букв с форума поиском все же удалось найти ответ Рената

Как осуществить третий пункт?! 

Тоже заинтересовался, откуда берётся надпись "Установлен по ТР"? Сработал Тейк Профит,

Ордер, который был размещён после срабатывания тейк профит

и ордер, который закрыл позицию:

Загадочная надпись "Установлен по ТР"

А вот абсолютно все свойства данного ордера:

#Первоначальный объем 0.01 Невыполненный объем 0 Цена, указанная в ордере 1.10582 Уровень Stop Loss 0
#Уровень Take Profit 0 Текущая цена по символу ордера 1.10582 Цена постановки Limit ордера при срабатывании StopLimit ордера 0
#Время постановки ордера 2016.07.18 14:18  Тип ордера ORDER_TYPE_BUY_STOP  Статус ордера ORDER_STATE_FILLED Время истечения ордера 2016.07.18 14:18
#Время исполнения или снятия ордера 2016.07.18 14:18  Время установки ордера на исполнение в миллисекундах -27308725
#Время исполнения/снятия ордера в миллисекундах с 01.01.1970 -27308715  Тип исполнения по остатку ORDER_FILLING_IOC
#Время жизни ордера ORDER_TIME_GTC  Идентификатор эксперта выставившего ордер 0 Идентификатор позиции 91048732 Идентификатор встречной позиции 0
#Символ EURUSD  Комментарий  [tp 1.10582]

Свойства ордера получаю скриптом:

//+------------------------------------------------------------------+
//|                                    GetHistoryOrderProperties.mq5 |
//|                              Copyright © 2016, Vladimir Karputov |
//|                                           http://wmua.ru/slesar/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2016, Vladimir Karputov"
#property link      "http://wmua.ru/slesar/"
#property version   "1.00"
#property description "Скрипт показывает абсолютно все свойства ордера из истории"
#property script_show_inputs

input ulong order_ticket=0; // ticket of order
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   ResetLastError();
   if(!HistoryOrderSelect(order_ticket))
     {
      Print("Error history select order № ",order_ticket,", #",GetLastError());
      return;
     }
//--- переменные для получения значений из свойств ордера 
   ulong  ticket=order_ticket;
   double volume_initial=0;  //Первоначальный объем при постановке ордера
   double volume_current=0;  //Невыполненный объем
   double price_open=0;      //Цена, указанная в ордере
   double SL=0;              //Уровень Stop Loss
   double TP=0;              //Уровень Take Profit
   double price_current=0;   //Текущая цена по символу ордера
   double price_stoplimit=0; //Цена постановки Limit ордера при срабатывании StopLimit ордера

//--- получим свойства ордера 
   volume_initial=HistoryOrderGetDouble(ticket,ORDER_VOLUME_INITIAL);
   volume_current=HistoryOrderGetDouble(ticket,ORDER_VOLUME_CURRENT);
   price_open=HistoryOrderGetDouble(ticket,ORDER_PRICE_OPEN);
   SL=HistoryOrderGetDouble(ticket,ORDER_SL);
   TP=HistoryOrderGetDouble(ticket,ORDER_TP);
   price_current=HistoryOrderGetDouble(ticket,ORDER_PRICE_CURRENT);
   price_stoplimit=HistoryOrderGetDouble(ticket,ORDER_PRICE_STOPLIMIT);

//--- подготовим и выведм информацию об ордере 
   printf("#Первоначальный объем %G Невыполненный объем %G Цена, указанная в ордере %G Уровень Stop Loss %G",
          volume_initial,volume_current,price_open,SL);
   printf("#Уровень Take Profit %G Текущая цена по символу ордера %G Цена постановки Limit ордера при срабатывании StopLimit ордера %G",
          TP,price_current,price_stoplimit);

//--- переменные для получения значений из свойств ордера 
   datetime time_setup=0;                    //Время постановки ордера
   ENUM_ORDER_TYPE type=0;                   //Тип ордера
   ENUM_ORDER_STATE state=0;                 //Статус ордера
   datetime time_expiration=0;               //Время истечения ордера
   datetime time_done=0;                     //Время исполнения или снятия ордера
   long time_setup_msc=0;                    //Время установки ордера на исполнение в миллисекундах с 01.01.1970
   long time_done_msc=0;                     //Время исполнения/снятия ордера в миллисекундах с 01.01.1970
   ENUM_ORDER_TYPE_FILLING type_filling=0;   //Тип исполнения по остатку
   ENUM_ORDER_TYPE_TIME type_time=0;         //Время жизни ордера
   long magic=0;                             //Идентификатор эксперта выставившего ордер (предназначен для того, чтобы каждый эксперт выставлял свой собственный уникальный номер)
   long position_id=0;                       //Идентификатор позиции, который ставится на ордере при его исполнении. Каждый исполненный ордер порождает сделку, которая открывает новую или изменяет уже существующую позицию. Идентификатор этой позиции и устанавливается исполненному ордеру в этот момент.
   long position_by_id=0;                    //Идентификатор встречной позиции для ордеров типа ORDER_TYPE_CLOSE_BY

//--- получим свойства ордера 
   time_setup=(datetime)HistoryOrderGetInteger(ticket,ORDER_TIME_SETUP);
   type=(ENUM_ORDER_TYPE)HistoryOrderGetInteger(ticket,ORDER_STATE);
   state=(ENUM_ORDER_STATE)HistoryOrderGetInteger(ticket,ORDER_STATE);
   time_expiration=(datetime)HistoryOrderGetInteger(ticket,ORDER_TIME_EXPIRATION);
   time_done=(datetime)HistoryOrderGetInteger(ticket,ORDER_TIME_DONE);
   time_setup_msc=HistoryOrderGetInteger(ticket,ORDER_TIME_SETUP_MSC);
   time_done_msc=HistoryOrderGetInteger(ticket,ORDER_TIME_DONE_MSC);
   type_filling=(ENUM_ORDER_TYPE_FILLING)HistoryOrderGetInteger(ticket,ORDER_TYPE_FILLING);
   type_time=(ENUM_ORDER_TYPE_TIME)HistoryOrderGetInteger(ticket,ORDER_TYPE_TIME);
   magic=HistoryOrderGetInteger(ticket,ORDER_MAGIC);
   position_id=HistoryOrderGetInteger(ticket,ORDER_POSITION_ID);
   position_by_id=HistoryOrderGetInteger(ticket,ORDER_POSITION_BY_ID);

//--- подготовим и выведм информацию об ордере 
   printf("#Время постановки ордера %s  Тип ордера %s  Статус ордера %s Время истечения ордера %s",
          TimeToString(time_setup),EnumToString(type),EnumToString(state),TimeToString(time_expiration));
   printf("#Время исполнения или снятия ордера %s  Время установки ордера на исполнение в миллисекундах %d",
          TimeToString(time_done),time_setup_msc);
   printf("#Время исполнения/снятия ордера в миллисекундах с 01.01.1970 %d  Тип исполнения по остатку %s",
          time_done_msc,EnumToString(type_filling));
   printf("#Время жизни ордера %s  Идентификатор эксперта выставившего ордер %d Идентификатор позиции %d Идентификатор встречной позиции %d",
          EnumToString(type_time),magic,position_id,position_by_id);

//--- переменные для получения значений из свойств ордера   
   string symbol=NULL;                       //Символ, по которому открыта позиция
   string comment=NULL;                      //Комментарий к позиции

//--- получим свойства ордера 
   symbol=HistoryOrderGetString(ticket,ORDER_SYMBOL);
   comment=HistoryOrderGetString(ticket,ORDER_COMMENT);
//--- подготовим и выведм информацию об ордере 
   printf("#Символ %s  Комментарий  %s",
          symbol,comment);
  }
//+------------------------------------------------------------------+ 
//| Возвращает строковое наименование типа ордера                    | 
//+------------------------------------------------------------------+ 
string GetOrderType(long type)
  {
   string str_type="unknown operation";
   switch((int)type)
     {
      case (ORDER_TYPE_BUY):            return("buy");
      case (ORDER_TYPE_SELL):           return("sell");
      case (ORDER_TYPE_BUY_LIMIT):      return("buy limit");
      case (ORDER_TYPE_SELL_LIMIT):     return("sell limit");
      case (ORDER_TYPE_BUY_STOP):       return("buy stop");
      case (ORDER_TYPE_SELL_STOP):      return("sell stop");
      case (ORDER_TYPE_BUY_STOP_LIMIT): return("buy stop limit");
      case (ORDER_TYPE_SELL_STOP_LIMIT):return("sell stop limit");
     }
   return(str_type);
  }
//+------------------------------------------------------------------+
Файлы:
 
Предлагаю разработчикам одно из решений проблемы.

Ордеру, который создал OUT-сделку для закрытия (частичного) позиции, автоматом прописывать его TP и SL поля значениями соответствующих полей позиции. Тогда всегда будет возможность определить SL/TP закрытой позиции. Сделать это самому искусственно возможно, но не всегда - можно нарваться на Invalid Stops, например. Или когда позиция закрывается по MarginCall, SL или TP. Поэтому торговый сервер сам должен прописывать SL/TP поля ордеру, который вызывает закрывающую (частично) OUT-сделку позиции.
 
И хорошо бы ордер, вызывающий OUT-сделку закрытия (частичного) позиции по ее (позиции) TP сделать не маркет-ордером, а лимитным.
 
fxsaber:
До введения хэджа TakeProfit открытых позиций хранились не на бирже в виде лимитных ордеров, а на MT5-торговом сервере. При этом в момент акцепта они отправлялись на биржу не как лимитные ордера по заявленной цене, а как маркет-ордера по заявленной цене.
А если по маркету будет отказ, тогда что? Лимитный трансформировался в маркет, а маркет не исполнился!?! И в результате - остались без TP?
 
fxsaber:

Частичное исполнение лимитных ордеров - посмотрите в гуле. Это все работает даже в MT4.

Если вы не заказывали частичное исполнение - то откуда оно появится?

EURUSD, допустим текущая цена 1.10, TP на 1.105. Шпилька на 1.106. Полетел рыночный ордер - отказ. Снова текущая цена 1.10

 
fxsaber:
Сейчас MT5 и шлет рыночный ордер за TP. А мог бы отправлять - лимитный. Получил реквот - лимитный ордер продолжает висеть: TP никуда не делся. Говорю же, изучите вопрос для начала.
С терминологией определитесь: "маркет-ордер по заявленной цене" - это и есть лимитный ордер
 
fxsaber:
Мне сложно рекомендовать Вам литературу по Основам. Кто-нибудь с форума, возможно, поможет. Меня Вы слышать не хотите. Удачи!

"После прочтения тонн букв с форума поиском все же удалось найти..."

До свидания, "доктор" (только ник поменял)

Причина обращения: