Библиотеки: MT4Orders - страница 32

 
// Список изменений:
// 08.02.2019
//   Add: Комментарий позиции сохраняется при частичном закрытии через OrderClose.
//        Если нужно сменить комментарий открытой позиции при частичном закрытии, в OrderClose можно его задать.
 

Бывают задачи, которые нельзя решить только средствами MQL4.

Но MT4-style и MT5-style можно использовать параллельно. Пример такого совмещения.

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

Обработка транзакций OnTradeTransaction

fxsaber, 2019.02.08 12:37

Задача

На Неттинге выставлены отложенные ордера (могут быть разнонаправленные и каждого типа любое количество). Нужно при каждом срабатывании исходного отложенного ордера выставлять свои SL/TP в виде Stop/Limit отложенных ордеров. При этом SL/TP ордера должны быть зависимы: сработал один - удалился второй. Исходные и SL/TP отложенные ордера могут срабатывать частично. Советник может в любой момент перезагружаться, включая перенос на другой Терминал.


Решение

#include <MT4Orders.mqh> // https://www.mql5.com/ru/code/16006

input int inTP = 100;
input int inSL = 200;
sinput MAGIC_TYPE inMagicNumber = 0;
sinput string inStrKey = "SLTP";

int GetAmountDeals()
{
  return(HistorySelect(0, INT_MAX) ? HistoryDealsTotal() : 0);
}

bool IsMyString( const string Str, const string Key )
{
  return(StringSubstr(Str, 0, StringLen(Key)) == Key);
}

string ToMyString( const string Str, const string Key )
{
  return(Key + Str);
}

struct ORDER
{
  int Type;
  TICKET_TYPE Ticket;
  double Lots;
  double OpenPrice;
  MAGIC_TYPE Magic;
  string Comment;
  
  ORDER() : Type(OrderType()), Ticket(OrderTicket()), Lots(OrderLots()),
            OpenPrice(OrderOpenPrice()), Magic(OrderMagicNumber()), Comment(OrderComment())
  {
  }
};

#define _CS(A) ((!::IsStopped()) && (A))

bool GetPairOrder()
{
  bool Res = false;

  ORDER Order;
  Order.Type = 6 - Order.Type + ((Order.Type & 1) << 1);
  
  for (int i = OrdersTotal() - 1; _CS((i >= 0) && (!Res)); i--)
    Res = OrderSelect(i, SELECT_BY_POS) && (OrderType() == Order.Type) &&
          (OrderMagicNumber() == Order.Magic) && (OrderComment() == Order.Comment);
    
  return(Res);
}

void CheckSLTP( const string Symb, const MAGIC_TYPE Magic, const string Key, const int Slip = 100 )
{    
  for (int i = OrdersTotal() - 1; _CS(i >= 0); i--)
    if (OrderSelect(i, SELECT_BY_POS) && (OrderType() > OP_SELL)  &&
        (OrderMagicNumber() == Magic) && (OrderSymbol() == Symb) && IsMyString(OrderComment(), Key))
    {
      const ORDER Order;      
      
      if (!_CS(GetPairOrder()))
      {
        OrderDelete(Order.Ticket);
        
        i = OrdersTotal();
      }
      else if (_CS(OrderLots() < Order.Lots))
      {
        if (OrderDelete(Order.Ticket))
          OrderSend(OrderSymbol(), Order.Type, OrderLots(), Order.OpenPrice, Slip, 0, 0, Order.Comment, Order.Magic);
          
        i = OrdersTotal();          
      }
    }
}

void CheckFill( const string Symb, const MAGIC_TYPE Magic, const string Key, const int SL, const int TP )
{    
  static int PrevDeals = GetAmountDeals();
  
  const double point = SymbolInfoDouble(Symb, SYMBOL_POINT);
  const int NewDeals = GetAmountDeals();
  
  while (_CS(PrevDeals < NewDeals))
  {
    const ulong Ticket = HistoryDealGetTicket(PrevDeals);
    
    if (Ticket && (HistoryDealGetInteger(Ticket, DEAL_MAGIC) == Magic) &&
                  (HistoryDealGetString(Ticket, DEAL_SYMBOL) == Symb) &&
                  !IsMyString(HistoryDealGetString(Ticket, DEAL_COMMENT), Key))
    {
      const double Lots = HistoryDealGetDouble(Ticket, DEAL_VOLUME);
      const double Price = HistoryDealGetDouble(Ticket, DEAL_PRICE);
      const int Type = 1 - (int)HistoryDealGetInteger(Ticket, DEAL_TYPE);
      const double Koef = Type ? -point : point;
      const string Comment = ToMyString((string)Ticket, Key);
      
      if (OrderSend(Symb, Type + OP_BUYLIMIT, Lots, Price - Koef * TP, 0, 0, 0, Comment))
        OrderSend(Symb, Type + OP_BUYSTOP, Lots, Price + Koef * SL, 0, 0, 0, Comment);
    }
    
    PrevDeals++;
  }
}

void System()
{
  CheckFill(_Symbol, inMagicNumber, inStrKey, inSL, inTP);
  CheckSLTP(_Symbol, inMagicNumber, inStrKey);
}

void OnTrade()
{
  System();
}

void OnInit()
{
  OnTrade();
}
 

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

Библиотеки: MT4Orders

fxsaber, 2019.01.13 17:23

Кимовские функции под MT4 довольно популярны, поэтому скачал все исходники с его сайта и написал для них простой  "конвертер" под MT5.
#include <KimIVToMT5.mqh> // https://c.mql5.com/3/263/KimIVToMT5.mqh

#include "e-Trailing.mq4" // http://www.kimiv.ru/index.php?option=com_remository&Itemid=13&func=fileinfo&id=14

void OnTick() { start(); }

Оказалось, что кимовский конвертер делает рабочими и некоторые другие MT4-советники под MT5

#include <KimIVToMT5.mqh> // https://c.mql5.com/3/263/KimIVToMT5.mqh

#include "Reaction.mq4"   // https://www.mql5.com/ru/code/24609

void OnTick() { start(); }
 
// Список изменений:
// 20.02.2019
//   Fix: В случае отсутствия MT5-ордера от существующей MT5-сделки библиотека будет ожидать синхронизации истории. В случае неудачи сообщит об этом.


Произведен обход этого подводного камня на MT5

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

Новая версия платформы MetaTrader 5 build 2005: Экономический календарь, MQL5-программы в виде сервисов и API для языка R

fxsaber, 2019.02.20 21:06

Привет Сервисам
// Скрипт ловит ситуации, когда ордер сделки отсутствует в истории.

#define _CS(A) ((!::IsStopped()) && (A))
#define TOSTRING(A) #A + " = " + (string)(A) + " "
#define OFFSET 100

void OnStart()
{
  datetime PrevTime = OFFSET;
  
  while (_CS(true))
  {
    if (HistorySelect(PrevTime - OFFSET, INT_MAX))
    {
      const int Total = HistoryDealsTotal();
      
      for (int i = 0; _CS(i < Total); i++)
      {
        const ulong DealTicket = HistoryDealGetTicket(i);
        const ulong OrderTicket = HistoryDealGetInteger(DealTicket, DEAL_ORDER);        
        
        int Count = 0;
        const ulong StartTime = GetMicrosecondCount();
        
        while ((HistoryOrderGetInteger(OrderTicket, ORDER_TICKET) != OrderTicket) && !HistoryOrderSelect(OrderTicket)) // Если нет ордера сделки
          Count++;
        
        if (Count)
          Alert(TOSTRING(DealTicket) + TOSTRING(OrderTicket) + TOSTRING(Count) + TOSTRING(GetMicrosecondCount() - StartTime)); // Распечатываем ситуацию
        
        PrevTime = (datetime)HistoryDealGetInteger(DealTicket, DEAL_TIME);
      }
    }
    
    Sleep(0); // Без этого Терминал мгновенно вешается.
  }
}


Если закомментировать Sleep, то запуском мгновенно убьете Терминал. Но речь пойдет о другом.

Оказалось, что элементарно поймать ситуацию, когда ордер сделки отсутствует в истории: сделка есть, а ее ордер - нет.

Результат работы скрипта на MQ-Demo

Alert: DealTicket = 336236873 OrderTicket = 356249474 Count = 1614408 GetMicrosecondCount()-StartTime = 229880 
Alert: DealTicket = 336236882 OrderTicket = 356249486 Count = 1565921 GetMicrosecondCount()-StartTime = 229998 
Alert: DealTicket = 336236887 OrderTicket = 356249492 Count = 1559345 GetMicrosecondCount()-StartTime = 229788 
Alert: DealTicket = 336236898 OrderTicket = 356249505 Count = 157107 GetMicrosecondCount()-StartTime = 22878 
Alert: DealTicket = 336236901 OrderTicket = 356249508 Count = 1544271 GetMicrosecondCount()-StartTime = 220879 

Больше 200мс нужно ждать появления в истории ордера сделки! Все это время невозможно определить, например, проскальзывание и длительность исполнения.

Представьте себе ситуацию, что нужен Сервис, который пишет в файл данные по только что закрытым позициям. Из-за этой "фичи" сделать это просто не получится.


Правильно ли понимаю, что эту архитектурную особенность MT5 никак не поправить?


Сложно уже посчитать, сколько в библиотеке учтено подобных вещей.

 
Andrey Khatimlianskii:

Да, но не видно — в чем они. Я покажу, когда сделаю.


Именно. Зипы не обновляются (

Когда скачиваешь файлы по-одному, теряются даты модификации. Приходится ориентироваться по размерам, но не все исправления меняют размер.

Такое предложение:

Добавлять в шапке КАЖДОГО файла дату модификации, опционально - номер версии, необязательно - история изменений.

Дополнительно к файлам выкладывать свой zip/rar, как это делают некоторые.

 
bool IsTradeAllowed(void)
  {
   return(::MQLInfoInteger(::MQL_TRADE_ALLOWED));       // expression not boolean
  }
 
Edgar:

Библиотека содержится только в одном файле. Все остальное - мишура вокруг, которая очень давно не обновлялась.

Правда, есть еще OrderSend_Test2.mq5 - это стресс-тест MT5 и библы. Спамит торговый сервер так, что тот отрубает автоторговлю.

 

Я знаю. В данном случае - да. Правда, с прошлого раза поменялось 3 файла.

Я про общую методику, которая упрощает versioning. Я сам всегда добавляю дату в шапку mqh, дату и версию в mq5.

И включение своего полного архива (для сохранения дат модификаций) позволит обновлять всё не задумываясь.
 
Edgar:

Я знаю. В данном случае - да. Правда, с прошлого раза поменялось 3 файла.

Я про общую методику, которая упрощает versioning. Я сам всегда добавляю дату в шапку mqh, дату и версию в mq5.

Мне предлагали публичную уже готовую под КБ-работу аналог гитхаба, где все это есть в автоматическом режиме. Но старость - не радость: сложно.

Возможно, для Вас такой вариант подойдет.

Объявляю об изменениях и даже шапку веду только у MT4Orders, т.к. не один пользуюсь.

Что же касается некоторых других работ - делаю это молча.

 

такую интересную особенность заметил


Есть у меня индикатор статистики (бесплатный) для мт4. 

решил сделать для мт5. 

библиотека как раз кстати! (самому писать такую библиотеку лень)


до этого у меня была версия  для МТ5 но она работала  с своими глюками. 


убрал весь MQL5 код - подключил библиотеку MT4Orders - и при запуске показывает ерунду 


при этом в истории все нормально:


и при распечатке OrderPrint все показывает верно (как в истории)


сделки у меня выбираются по номеру тикета (сложная запутанная система, сам путаюсь всегда и через время забываю, как это работает :-) )

вот кусок кода:

      if(SearchNextTicket(firsttimeticket,ticketREPORT,i,HistoryarrayNumber)==2)
        {
         firsttimeticket=MT45_OrderCloseTime();
        }
      ticketREPORT=MT45_OrderTicket();


Смысл в том, что следующий тикет (для статистики) выбирается по сортировке по времени закрытия, а если время одинаковое, то выбирается по номеру тикета.

так вот при таком раскладе показывает ерунду. 


но если я добавляю безобидную строку принта:

 Print("1=== i="+i+" firsttimeticket="+firsttimeticket+" ticketREPORT="+ticketREPORT+" HistoryarrayNumber="+HistoryarrayNumber);
      if(SearchNextTicket(firsttimeticket,ticketREPORT,i,HistoryarrayNumber)==2)
        {
         firsttimeticket=MT45_OrderCloseTime();
        }
      ticketREPORT=MT45_OrderTicket();

то все работает 



в чем магия Print ? 


такой принт и работает нормально:

       Print("1=== i="+i);


такой принт уже не работает нормально

       Print("1=== i=");
Причина обращения: