Особенности языка mql5, тонкости и приёмы работы - страница 70

fxsaber
14344
fxsaber  

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

Как определить замену графика?

fxsaber, 2018.02.08 12:39

void OnTick()
{  
  const long Chart = ChartID();
  string PrevSymbol = _Symbol;
  ENUM_TIMEFRAMES PrevTF = _Period;
    
  while (!IsStopped())
  {
    if ((PrevSymbol != ChartSymbol(Chart)) || (PrevTF != ChartPeriod(Chart))) // ноль указывать НЕЛЬЗЯ!
    {      
      PrevSymbol = ChartSymbol(Chart);
      PrevTF = ChartPeriod(Chart);
      
      Alert(PrevSymbol + " " + EnumToString(PrevTF));      
    }
    
    Sleep(0);
  }
}

Нулевой входной параметр ChartID в некоторых функциях не вызывает пересчет значений. Если нужны актуальные данные текущего чарта, нужно использовать полноценный ID.

fxsaber
14344
fxsaber  

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

POSITION_TICKET != POSITION_IDENTIFIER

fxsaber, 2018.02.12 20:14

Выводы

Если считать, что это штатное поведение MT5, а не особенности хака брокера, то

  • ORDER_STATE_PARTIAL у исторических ордеров не бывает.
  • Исполненнные ордера всегда имеют статус ORDER_STATE_FILLED.
  • При частичном исполнении создаются соответствующие новые маркет ордера торговым сервером (ORDER_REASON_CLIENT - даже если исходный ордер выставлен автоматом(EXPERT)).
  • Старый живой ордер (тикет не изменяется) остается висеть с уменьшенным объемом (ORDER_VOLUME_CURRENT).
  • Старый живой ордер при этом получает статус ORDER_STATE_PARTIAL. Фактически, этот флаг является результатом сравнения ORDER_VOLUME_CURRENT и ORDER_VOLUME_INITIAL.
  • Все открываемые позиции получают ID == OrderTicket. Где OrderTicket - тикеты, порождаемые торговым сервером.
  • Торговая сделка всегда имеет ровно один исторический ордер и его статус ORDER_STATE_FILLED.
  • Каждый исполненный исторический ордер имеет ровно одну сделку.
  • У любого исполненного ордера ORDER_VOLUME_INITIAL равен объему, на который он исполнился. Т.е. даже исходный раздербаненный ордер после полного исполнения имеет ORDER_VOLUME_INITITAL равный объему порожденной им сделки.
  • Время исходного ордера (который частично исполнялся) не меняется и не равно времени его сделки.
  • В исторической таблице идет сортировка по времени ордеров (ORDER_TIME_SETUP), не сделок. Поэтому если делать HistorySelect от DEAL_TIME, можно не получить в историческую таблицу соответствующий ордер.
  • HistorySelectByPosition всегда возвращает необходимое множество сделок/ордеров.
  • Для любой торговой сделки можно вычислить величину проскальзывания.

Недостатки

  • Не хватает ORDER_REASON_PARTIAL, DEAL_REASON_PARTIAL и POSITION_REASON_PARTIAL. При этом в соответствующих перечислениях эти флаги должны быть помещены сразу после REASON_EXPERT.
  • Соответствующие маркет-ордера при частичном исполнении лимитных ордеров по своей природе могут иметь отрицательное проскальзывание. Похоже, это ошибка только в типе ордеров и на самом деле маркет-ордера нет - создается только внутри MT5 и не идет наружу.

ЗЫ Полностью подтвержденная гипотеза

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

POSITION_TICKET != POSITION_IDENTIFIER

Pavel Kolchin, 2018.02.12 13:31

(не уверен, проверить сложно,по аналогии с частичным закрытием позиций)

Это все работает так:

1) отложенный ордер сработал частично - открылась позиция с Position_ID = Order_Ticket1

2) остаток ордера формируется в новый ордер Order_Ticket2 и ждет своего исполнения, новый Order_Ticket2   !=  Order_Ticket1, так как не может быть 2 ордера в истории с одинаковыми Order_Ticket

3) остаток ордера исполнился  - открылась позиция с Position_ID = Order_Ticket2

в истории два ордера в терминале две позиции, все соответствует

fxsaber
14344
fxsaber  

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

Обсуждение статьи "LifeHack для трейдера: замешиваем ForEach на дефайнах (#define)"

fxsaber, 2018.02.14 10:54

Замер производительности

#define BENCH(A)                                                              \
{                                                                             \
  const ulong StartTime = GetMicrosecondCount();                              \
  A;                                                                          \
  Print("Time[" + #A + "] = " + (string)(GetMicrosecondCount() - StartTime)); \
}

double GetAsk()
{
  static MqlTick tick = {0};
  
  return(SymbolInfoTick(Symbol(),tick) ? tick.ask : 0);
}

#define AMOUNT 1e6

void OnStart()
{
  double Sum = 0;
  
  BENCH(for (int i = 0; i < AMOUNT; i++) Sum += GetAsk())
  BENCH(for (int i = 0; i < AMOUNT; i++) Sum += SymbolInfoDouble(_Symbol, SYMBOL_ASK))
  
  Print(Sum);
}


Результат

Time[for(inti=0;i<AMOUNT;i++)Sum+=GetAsk()] = 78952
Time[for(inti=0;i<AMOUNT;i++)Sum+=SymbolInfoDouble(_Symbol,SYMBOL_ASK)] = 162606

Я был абсолютно не прав! SymbolInfoDouble в два раза медленнее SymbolInfoTick.

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

Обсуждение статьи "LifeHack для трейдера: замешиваем ForEach на дефайнах (#define)"

fxsaber, 2018.02.14 11:58

Некомпетентен. Результат в Тестере

2017.09.01 00:00:10   Time[for(inti=0;i<AMOUNT;i++)Sum+=GetAsk()] = 87424
2017.09.01 00:00:10   Time[for(inti=0;i<AMOUNT;i++)Sum+=SymbolInfoDouble(_Symbol,SYMBOL_ASK)] = 83410

Там, где нужна производительность (Оптимизатор), лучше использовать SymbolInfoDouble. На реале - без разницы.


ЗЫ Замер скорости функций нужно измерять в среде, где производительность важна - Тестер.

fxsaber
14344
fxsaber  

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

Ошибки, баги, вопросы

fxsaber, 2018.02.12 23:10

Открываю руками BUY-позицию на двух торговых демо-серверах


RoboForex-MetaTrader 5

2018.02.13 00:02:08.424 '8520459': market buy 1.00 GBPUSD
2018.02.13 00:02:10.101 '8520459': accepted market buy 1.00 GBPUSD
2018.02.13 00:02:10.101 '8520459': deal #90389019 buy 1.00 GBPUSD at 1.38387 done (based on order #107426544)
2018.02.13 00:02:10.101 '8520459': order #107426544 buy 1.00 / 1.00 GBPUSD at 1.38387 done in 1683.949 ms


FXOpen-MT5

2018.02.13 00:00:25.780 '18000903': market buy 1.00 GBPUSD
2018.02.13 00:00:25.912 '18000903': accepted market buy 1.00 GBPUSD
2018.02.13 00:00:25.922 '18000903': market buy 1.00 GBPUSD placed for execution
2018.02.13 00:00:25.942 '18000903': order #896454 buy 1.00 / 1.00 GBPUSD at market done in 154.252 ms
2018.02.13 00:00:25.942 '18000903': deal #80559 buy 1.00 GBPUSD at 1.38387 done (based on order #896454)

Строки одного цвета обозначают одно и то же. Однако, хорошо видно, что они идут в разных очередностях. У Робо сообщение о выполнении приказа приходит после выполнения сделки. А у опенов - ДО! По этой причине OrderSend возвращает удачу, но сделки еще нет. Т.е. получаем несинхронизированность OrderSend с историей

Код для FXOpen-MT5

#define PRINT(A) Print(#A + " = " + (string)(A))

void OnStart()
{
  MqlTradeRequest Request = {0};
  
  Request.action = TRADE_ACTION_DEAL;
  Request.symbol = _Symbol;
  Request.volume = 1;
  Request.type_filling = ORDER_FILLING_IOC;
  
  MqlTradeResult Result;
  
  PRINT(OrderSend(Request, Result));
  PRINT(Result.deal);
}


Результат

OrderSend(Request,Result) = true
Result.deal = 0


Данная ситуация имеет следующее объяснение

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

Ошибки, баги, вопросы

Rashid Umarov, 2018.02.15 06:25

Если ордер отправдяется во внешнюю торговую систему, то торговый сервер MetaTrader 5 не ждет ответа от неё и сразу возвращает результат запроса как "ордер размещен". По этой причине OrderSend всегда вернет deal=0, так как информации о совершенной сделке еще нет. Ловите её в OnTrade или OnTradeTransaction.

Пример слушателя торговых событий дан в статье С чего начать при создании торгового робота для Московской биржи MOEX - TradeTransactionListener.mq5

OrderSend - отправил ордер на совершение рыночной сделки. Ордер выставлен - для этого надо ознакомиться с Result.order. А вот со сделку(и) никто не дожидается - их может быть много и общее время их исполнения, вообще то не определено.

Зависит от конкретной реализации вывода на стороне брокера. В общем случае не определено.

Поэтому очень рекомендую в качестве теста своего кода использовать и демо-счет на FXOpen-MT5, т.к. он стоит совсем особняком, по сравнению с другими демо.


Например, предлагаю попробовать написать на MQL5 скрипт с такой торговой логикой (MQL4-стиль только для быстрого показа смысла)

void OnStart()
{
  OrderCloseBy(OrderSend(_Symbol, OP_BUY, 1, Ask, 0, 0, 0), OrderSend(_Symbol, OP_SELL, 1, Bid, 0, 0, 0));
}

Это совсем не просто. Так же рекомендую упомянутый демо-сервер для отработки частичного исполнения.

Artyom Trishkin
Модератор
123331
Artyom Trishkin  
fxsaber:
Удалили пост, где было дано объяснение одной из самых частых ошибок на MT5.
Нет его среди удалённых. Странно. Ещё раз можете запостить?
Artyom Trishkin
Модератор
123331
Artyom Trishkin  
fxsaber:

Пост большим был. Не ожидал, что удалят. Хотелось бы услышать причину удаления. Потому как повторно нарваться на удаление - мазохизм.

Говорю же - нет среди удалённых. Может сбой какой имел место быть?
fxsaber
14344
fxsaber  

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

Организация цикла перебора ордеров

fxsaber, 2018.02.16 09:40

в MT5 все совсем не в порядке. Пример, показывающий проблему

// Пример неправильного считывания торгового окружения на каждом тике
// Скрипт эмулирует два тика ТС, которая должна открыть одну позицию, если ее нет.

#include <Trade/Trade.mqh>

// Возвращает количество позиций по символу
int GetAmountPositions( const string Symb )
{
  int Res = 0;
  
  // Этот MQL5-код с ошибкой
  for (int i = PositionsTotal() - 1; i >= 0; i--)
    if (PositionGetSymbol(i) == Symb)
      Res++;

/*
  // В MT4 такой код выполняется без ошибки
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS) && (OrderType() <= OP_SELL) && (OrderSymbol() == Symb))
      Res++;
*/      
  return(Res);
}

// Пример OnTick
void ExampleOnTick()
{
  static CTrade Trade;
  
  // Если нет позиции, открываем
  if (!GetAmountPositions(_Symbol))
    Trade.Buy(1);    
}

// Эмуляция прихода двух Tick-событий
void OnStart()
{
  ExampleOnTick(); 
  
  Sleep(10); // Между двумя тиками ~10 мс.
  
  ExampleOnTick();
}

Как думаете, если запустить этот скрипт на символе без позиций, что будет в итоге?

Правильный ответ: будет открыта одна или две позиции.

Причина, почему так происходит. После первого OrderSend появляется маркет-ордер и если до момента его исполнения приходит новый тик, то позиции еще нет и делается второй OrderSend.

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

Неплохая на первый взгляд идея PositionsTotal несколько омрачается необходимостью в MT5 еще анализировать и OrdersTotal на предмет маркет-ордеров.

Будьте аккуратны!

fxsaber
14344
fxsaber  
fxsaber:

В связи с этим не будет правильно работать казалось бы нормальный MT5-шаблон и, как следствие, большинство MT5-советников в кодобазе.

В качестве доказательства этого утверждения можно взять почти любой советник из MT5-кодобазы. Не будем что-то выискивать, а сразу возьмем самый свежий советник на данный момент. Хорошо, что он написан автором с большим MT5-опытом публикаций в КБ.

В исходнике такие строки (свои комментарии выделил)

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//....
   int total=0; // для расчета количества открытых советником позиций
//....
//--- main cycle
   for(int i=PositionsTotal()-1;i>=0;i--)
      if(m_position.SelectByIndex(i)) // selects the position by index for further access to its properties
         if(m_position.Symbol()==m_symbol.Name() && m_position.Magic()==m_magic)
           {
            total++; // расчет количества открытых позиций
//....
   if(total==0) // Если нет открытых советником позиций
     {
      if(!RefreshRates())
        {
         PrevBars=iTime(1);
         return;
        }
      //--- open BUY 
      if(MACD_MAIN_2>MACD_SIGNAL_2 && MACD_MAIN_4<MACD_SIGNAL_4) // Сигнал на покупку
        {
         double sl=(InpStopLoss!=0)?m_symbol.Ask()-ExtStopLoss:0.0;
         double tp=(InpTakeProfit!=0)?m_symbol.Ask()+ExtTakeProfit:0.0;
         OpenBuy(sl,tp); // Отправка маркет-ордера на покупку
         return;
        }
      //--- open SELL
      if(MACD_MAIN_2<MACD_SIGNAL_2 && MACD_MAIN_4>MACD_SIGNAL_4) // Сигнал на продажу
        {
         double sl=(InpStopLoss!=0)?m_symbol.Bid()+ExtStopLoss:0.0;
         double tp=(InpTakeProfit!=0)?m_symbol.Bid()-ExtTakeProfit:0.0;
         OpenSell(sl,tp); // Отправка маркет-ордера на продажу
        }
     }
   return;
  }

Получаем идентичную ситуацию, что была описана

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

Особенности языка mql5, тонкости и приёмы работы

fxsaber, 2018.02.16 19:52

После первого OrderSend появляется маркет-ордер и если до момента его исполнения приходит новый тик, то позиции еще нет и делается второй OrderSend.

Неплохая на первый взгляд идея PositionsTotal несколько омрачается необходимостью в MT5 еще анализировать и OrdersTotal на предмет маркет-ордеров.

Это значит, что в общем случае советник откроет вместо одной позиции две, три и т.д. В зависимости от того, как часто будут приходить тики и сколько времени будут исполняться маркет-ордера.


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

MACD EA
MACD EA
  • голосов: 4
  • 2018.02.15
  • Vladimir Karputov
  • www.mql5.com
При поступлении сигнала противоположная позиция закрывается. Также советник может закрывать половину позиции (параметр Profit for closing half of the position), может переводить позицию в безубыток (параметр Breakeven). Размер открываемой позиции может задавать вручную (параметр Lots) или в процентах риска от свободной маржи (параметр Risk in...
fxsaber
14344
fxsaber  

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

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

Ошибки, баги, вопросы

fxsaber, 2018.02.14 08:58

Вся череда событий OnTradeTransaction идет после завершения OrderSend.

Советник

void OnTradeTransaction ( const MqlTradeTransaction &Trans, const MqlTradeRequest&, const MqlTradeResult& )
{ 
  static bool FirstRun = true;  
  static ulong StartTime;
  
  if (FirstRun)
  {
    StartTime = GetMicrosecondCount();
    
    FirstRun = false;
  }

  Print(EnumToString(Trans.type));
  Print((GetMicrosecondCount() - StartTime) / 1000);    
}

Руками отправляем торговый приказ.

Лог

2018.02.14 09:41:46.671 '8854170': instant sell 1.00 EURUSD at 1.23673
2018.02.14 09:41:46.853 '8854170': accepted instant sell 1.00 EURUSD at 1.23673
2018.02.14 09:41:46.853 '8854170': deal #192088422 sell 1.00 EURUSD at 1.23673 done (based on order #208541700)
2018.02.14 09:41:46.853 '8854170': order #208541700 sell 1.00 / 1.00 EURUSD at 1.23673 done in 190.608 ms


Результат советника

2018.02.14 09:41:46.853 TRADE_TRANSACTION_ORDER_ADD
2018.02.14 09:41:46.853 0
2018.02.14 09:41:46.853 TRADE_TRANSACTION_DEAL_ADD
2018.02.14 09:41:46.853 1
2018.02.14 09:41:46.853 TRADE_TRANSACTION_ORDER_DELETE
2018.02.14 09:41:46.853 1
2018.02.14 09:41:46.853 TRADE_TRANSACTION_HISTORY_ADD
2018.02.14 09:41:46.853 2
2018.02.14 09:41:46.853 TRADE_TRANSACTION_REQUEST
2018.02.14 09:41:46.853 2


Отлично видно и по столбцу времени и по числовым показателям советника, что длительность выполнения торгового приказа никак не сказывается на череде событий OnTradeTransaction. Вся асинхронность летит к чертям! Умудрились же так напортачить. Build 1755.

Например, при выставлении маркет-ордера OrderSendAsync в Терминале маркет-ордер даже на мгновение не появится. Возможно, разработчики решили так сделать для некоторого ускорения.

fxsaber
14344
fxsaber  

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

Обсуждение статьи "Визуализируем оптимизацию торговой стратегии в MetaTrader 5"

fxsaber, 2018.02.22 08:39

Во фрейм-режиме OnInit, OnDeinit, OnTick, OnTrade, OnTradeTransaction и OnTimer игнорятся. Только OnChartEvent пашет.

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