Новая версия платформы MetaTrader 5 build 6230: больше возможностей AI - страница 13

Для биржевого исполнения при срабатывании SL применяется цена последнего известного Ask\Bid. При этом тестер зачисляет на мой баланс лоты, с уровня, который был полностью выкуплен другим трейдером (старый уровень Bid|Ask). В результате часто получаются положительные проскальзывания. Это явная ошибка.

После обсуждения https://www.mql5.com/ru/forum/518455/page2#comment_60575043 пришли к выводу, что правильно использовать следующий Ask|Bid. 
Проблему уже не раз показывалась на форуме, но до сих пор тестер продолжает давать ложные результаты тестирования. Видимо разработчики не заметили. 
Вот и я замети проблему и описал со скриншотами и распечатками.


Прошу изучить и сделать работу тестера правильной.

Проскальзывание SL в тестере вызвано использованием Last вместо Ask.
Проскальзывание SL в тестере вызвано использованием Last вместо Ask.
  • 2026.10.15
  • www.mql5.com
Обнаружены проблемы с проскальзыванием при срабатывании стоп-лоссов в тестере, связанные с использованием цены Last вместо Ask. Сделки закрываются по старым ценам, что приводит к положительным проскальзываниям. Рекомендуется использовать цену Ask, запущенную срабатыванием SL, или следующий Ask, а также учитывать клиринговые времена и синхронизацию часов.
Aleksei Kuznetsov #:

Для биржевого исполнения при срабатывании SL применяется цена последнего известного Ask\Bid. При этом тестер зачисляет на мой баланс лоты, с уровня, который был полностью выкуплен другим трейдером (старый уровень Bid|Ask). В результате часто получаются положительные проскальзывания. Это явная ошибка.

После обсуждения https://www.mql5.com/ru/forum/518455/page2#comment_60575043 пришли к выводу, что правильно использовать следующий Ask|Bid. 
Проблему уже не раз показывалась на форуме, но до сих пор тестер продолжает давать ложные результаты тестирования. Видимо разработчики не заметили. 
Вот и я замети проблему и описал со скриншотами и распечатками.


Прошу изучить и сделать работу тестера правильной.

Правильнее все таки использовать Last по которому тригернул SL. Почитайте обсуждение проблемы начиная с поста https://www.mql5.com/ru/forum/518455/page2#comment_60575043

Там же предложено простое для исправления решение.

b6272, как избавиться от логотипа в ChartScreenShot?
void OnStart()
{
  ChartScreenShot(0, "tmp.png", 300, 300);
}
fxsaber #:
b6272, как избавиться от логотипа в ChartScreenShot?

Полагаю, так же, как и от этого:


Т.е., никак. Это политика MQ.


Но, по идее, ИИ должен легко справиться )

fxsaber # :
b6272, ошибка во время дебага.
Строка для поиска :  Oshibka  172.
Подтверждено и передано в MetaQuotes.
Aleksei Kuznetsov #:

Для биржевого исполнения при срабатывании SL применяется цена последнего известного Ask\Bid. При этом тестер зачисляет на мой баланс лоты, с уровня, который был полностью выкуплен другим трейдером (старый уровень Bid|Ask). В результате часто получаются положительные проскальзывания. Это явная ошибка.

После обсуждения https://www.mql5.com/ru/forum/518455/page2#comment_60575043 пришли к выводу, что правильно использовать следующий Ask|Bid. 
Проблему уже не раз показывалась на форуме, но до сих пор тестер продолжает давать ложные результаты тестирования. Видимо разработчики не заметили. 
Вот и я замети проблему и описал со скриншотами и распечатками.


Прошу изучить и сделать работу тестера правильной.

Aleksei Kuznetsov #:

В таком случае, самое простое и правильное решение для MQ:

сделать триггер для SL не по цене Last как сейчас (для которой записан старый Ask|Bid), а просто по Ask|Bid ценам которые придут после нескольких Last (или без них), тогда автоматически применится актуальный Ask|Bid.

Вот рассмотрел как лимитный ордер срабатывает. Рассмотрел ордер в последней строке на этом скриншоте 

Время его активации 2025.09.12 07:03:29.114

Распечатка тиков и активации ордера:
2025.09.12 07:03:27   2025.09.12 07:03:27.683 130.03 130.09 130.09 1 1.0 2
2025.09.12 07:03:28   2025.09.12 07:03:28.9 130.03 130.09 130.03 5 5.0 344
2025.09.12 07:03:28   2025.09.12 07:03:28.963 130.03 130.09 130.03 1 1.0 344
2025.09.12 07:03:29   2025.09.12 07:03:29.111 130.03 130.09 130.03 191 191.0 344
2025.09.12 07:03:29   2025.09.12 07:03:29.111 130.03 130.09 130.03 300 300.0 344
2025.09.12 07:03:29   order [#18 buy limit 1 GAZP at 130.05] triggered
2025.09.12 07:03:29   deal #18 buy 1 GAZP at 130.05 done (based on order #18)
2025.09.12 07:03:29   deal performed [#18 buy 1 GAZP at 130.05]
2025.09.12 07:03:29   order performed buy 1 at 130.05 [#18 buy limit 1 GAZP at 130.05]
2025.09.12 07:03:29   2025.09.12 07:03:29.114 130.02 130.03 130.03 300 300.0 6
2025.09.12 07:03:29   2025.09.12 07:03:29.522 130.02 130.03 130.02 2 2.0 344

Видно, что время активации соответствует времени Ask/Bid тика. Так же логично, что сначала сработал ордер, а потом вызвался OnTick() и напечатал тик, т.е. однозначно активация не от Last тика.

А как показывал ранее SL срабатывает по Last тикам и берет неправильную устаревшую цену от предыдущего Bid/Ask тика, этот уровень в стакане уже давно вукуплен. Недоработка со стороны MQ. Нужно и стоповые ордеры по Ask/Bid тикам активировать, тогда не будет положительных проскальзываний по прошлым ценам.

Вот еще раз покажу и закрытие для того же ордера: (закрытие со скриншота в 2025.09.12 07:07:12.905)
2025.09.12 07:07:12   2025.09.12 07:07:12.592 130.16 130.2 130.19 85 85.0 4
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.16 10 10.0 344
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.15 1 1.0 344
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.14 1 1.0 344
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.14 1 1.0 344
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.13 129 129.0 344
2025.09.12 07:07:12   stop loss triggered #18 buy 1 GAZP 130.05 sl: 130.12 [#19 sell 1 GAZP at 130.12]
2025.09.12 07:07:12   deal #19 sell 1 GAZP at 130.16 done (based on order #19)
2025.09.12 07:07:12   deal performed [#19 sell 1 GAZP at 130.16]
2025.09.12 07:07:12   order performed sell 1 at 130.16 [#19 sell 1 GAZP at 130.12]
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.12 258 258.0 344
2025.09.12 07:07:12   2025.09.12 07:07:12.907 130.12 130.19 130.12 258 258.0 6

Видно что SL по Last тику сработал.

Сравнение по суммарным проскальзываниям тестера MQ  с положительными проскальзываниями из за срабатывания SL по Last c ценами предыдущего Ask/Bid и    Virtual в котором SL берется от актуального Ask/Bid тика.

MQ по Last с старым Ask/Bid Virtual по новому Ask/Bid 

Результат в пипсах без учета проскальзываний -4615

Результат в пипсах без учета проскальзываний -4351

Результат без проскальзывания в пипсах близок (разница 264п), то есть стратегии работают почти одинаково в обоих тестерах.

А ошибочные положительные проскальзывания улучшают итог на +609 для MQ  и ухудшают на -1844 более приближенный к реальности результат из Virtual. Разница проскальзываний 2448.

И все это из за того, что берутся цены от прошлого Ask/Bid тика.

Без адекватного тестирования трейдеров не привлечь к торговле через MT5...
Aleksei Kuznetsov #:

ошибочные положительные проскальзывания улучшают итог на +609 для MQ 

Это MQ нужно искать биржевой счет в MT5, чтобы мочь воспроизвести.
fxsaber #:
Это MQ нужно искать биржевой счет в MT5, чтобы мочь воспроизвести.
Alain попросил - завтра предоставлю. И эксперта сделаю, который покажет положительные проскальзывания и другие найденные проблемы.
Aleksei Kuznetsov #:

А как показывал ранее SL срабатывает по Last тикам и берет неправильную устаревшую цену от предыдущего Bid/Ask тика, этот уровень в стакане уже давно вукуплен. Недоработка со стороны MQ. Нужно и стоповые ордеры по Ask/Bid тикам активировать, тогда не будет положительных проскальзываний по прошлым ценам.

Вот еще раз покажу и закрытие для того же ордера: (закрытие со скриншота в 2025.09.12 07:07:12.905)
2025.09.12 07:07:12   2025.09.12 07:07:12.592 130.16 130.2 130.19 85 85.0 4
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.16 10 10.0 344
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.15 1 1.0 344
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.14 1 1.0 344
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.14 1 1.0 344
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.13 129 129.0 344
2025.09.12 07:07:12   stop loss triggered #18 buy 1 GAZP 130.05 sl: 130.12 [#19 sell 1 GAZP at 130.12]
2025.09.12 07:07:12   deal #19 sell 1 GAZP at 130.16 done (based on order #19)
2025.09.12 07:07:12   deal performed [#19 sell 1 GAZP at 130.16]
2025.09.12 07:07:12   order performed sell 1 at 130.16 [#19 sell 1 GAZP at 130.12]
2025.09.12 07:07:12   2025.09.12 07:07:12.905 130.16 130.2 130.12 258 258.0 344
2025.09.12 07:07:12   2025.09.12 07:07:12.907 130.12 130.19 130.12 258 258.0 6

Видно что SL по Last тику сработал.

Эксперт для воспроизведения ошибки:

input datetime openTime    = D'2026.10.01 11:44:00';
input datetime closeTime   = D'2022.01.02 03:00:00';

input datetime printTiksFrom = D'2026.10.01 11:51:17';
input datetime printTiksTo   = D'2026.10.01 11:51:18';
input ENUM_ORDER_TYPE otype = 0;
input double lot = 1;
input int SL = 20;//SL in points
#include <Trade\Trade.mqh>
CTrade ExtTrade;
void OnTick(void){
   static MqlTick Tick; static bool opened=false, closed=false; 
   if (!SymbolInfoTick(_Symbol, Tick)){ return;}

   if(!opened && Tick.time>=openTime ){opened=true; ExtTrade.PositionOpen(_Symbol,otype,lot,(otype==ORDER_TYPE_SELL ? Tick.bid:Tick.ask),(otype==ORDER_TYPE_SELL ? Tick.ask+(SL*_Point):Tick.bid-(SL*_Point)),0);}
   if(closeTime>openTime && !closed && Tick.time>=closeTime){closed=true; ExtTrade.PositionClose(_Symbol);}
   
   if(Tick.time>= printTiksFrom && Tick.time< printTiksTo){ Print(Tick.time,".",Tick.time_msc%1000," bid:",Tick.bid," ask:",Tick.ask," last:",Tick.last," vol:",Tick.volume," flags:",Tick.flags,Flags(Tick.flags));}
}

void OnDeinit(const int reason){
   HistorySelect(0,TimeCurrent());
   uint     total=HistoryDealsTotal();
   ulong    ticket=0;double price,sl,slippage;datetime time;long type,entry;ulong msk;
   for(uint i=0;i<total;i++){
      if((ticket=HistoryDealGetTicket(i))>0){ //--- try to get deals ticket
         price =HistoryDealGetDouble(ticket,DEAL_PRICE);
         sl =HistoryDealGetDouble(ticket,DEAL_SL);
         time  =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);
         type  =HistoryDealGetInteger(ticket,DEAL_TYPE);
         msk  =HistoryDealGetInteger(ticket,DEAL_TIME_MSC);
         entry =HistoryDealGetInteger(ticket,DEAL_ENTRY);
           if(entry==DEAL_ENTRY_OUT){
              slippage=(type==0?sl-price:price-sl);
              Print(time,".",msk%1000," entry:OUT type:",(type==0?"sell->buy":"buy->sell")," sl:",sl," close price:",price," slippage:",(slippage>0?"+":""),DoubleToString(slippage,_Digits));
           }
        }
     }
     Print("OnTrade total:",total);
  }
  
string Flags(ulong f){
   string s="";
   if((f &TICK_FLAG_BID)   == TICK_FLAG_BID){s+=" BID";} 
   if((f &TICK_FLAG_ASK)   == TICK_FLAG_ASK){s+=" ASK";} 
   if((f &TICK_FLAG_SELL)  == TICK_FLAG_SELL){s+=" SELL";} 
   if((f &TICK_FLAG_BUY)   == TICK_FLAG_BUY){s+=" BUY";} 
   if((f &TICK_FLAG_LAST)  == TICK_FLAG_LAST){s+=" LAST";} 
   if((f &TICK_FLAG_VOLUME)== TICK_FLAG_VOLUME){s+=" VOLUME";} 
   return s;
}

Основные настройки

Настройки эксперта приложил в 4 .set файлах. Выбрал 4 случая, чтобы было ясно, что это не случайная ситуация, а регулярная.

Рассмотрим результат работы +slip buy.set
Происходит покупка и выход по SL

Триггером SL стала цена Last 98.07
Сделка совершена по цене 98.11 от старого Bid, все лоты по этой цене были выкуплены раньше/выше и для передачи их мне по SL не осталось. 
То есть проскальзывание +0.04 от старого Bid.
Должна применяться цена Bid 98.06 от нового тика Bid в последней строке скриншота. Проскальзывание было бы -0.01
----------------------------------------------------------


Рассмотрим результат работы +slip buy2.set

Всё то же самое. Проскальзывание +0.03

----------------------------------------------------------


Рассмотрим результат работы +slip sell.set

Проскальзывание +0.07. Тут была сделка продажи, поэтому для SL применяется цена Ask.

Таким образом видим, что из за триггера SL по цене Last, для исполнения SL применяется цена от старого Ask/Bid тика, все лоты которого уже выкуплены сделками показанными в Last.

Аналогично можно посмотреть исполнение лимитных ордеров, триггером для них служат Ask/Bid тики, а не Last. Считаю это правильным. И для стоповых ордеров триггером нужно использовать Ask/Bid тики, как показал выше.

----------------------------------------------------------


Рассмотрим результат работы -slippage.set

Здесь триггером так же является Last тик и применяется цена от старого Bid/Ask тика. 
Проскальзывание видим отрицательное: -0.10. 
Цена старого Bid = 97.87, Ask = 97.88. Но триггером SL стал Last Sell тик 97.97, который выше и Bid и Ask. На форуме писали, что если выставить лимитный Sell ордер выше Ask, то он исполняется об лимитный ордер Buy, который уже есть в стакане - видимо это и случилось. Возможно есть другие причины, почему  Last Sell тики идут выше и Bid и Ask. 
Если бы триггером были бы Ask/Bid тики, то SL сработал бы намного раньше, когда Bid первый раз стал ниже 97.97. 

Таким образом нашли вторую причину неправильности применения Last тиков для триггера SL (и других Stop ордеров) - она заключается в том, что SL не срабатывает по реальным заявкам в стакане когда Bid первый раз станет ниже SL. И может сработать позже на несколько секунд или минут, когда кто-то случайно отправил Buy Limit ордер выше Ask в стакане. Или позже на часы, пока цена снова не вернется и не появится новый Last тик по цене <= SL.

Доступ к счету для тестов отправлю Alain, его можно передать и сотрудникам MQ для исправления тестера. Надеюсь месяца хватит. Потом поменяю пароль, ну или позже, если попросят.

Файлы: